diff --git a/CMakeLists.txt b/CMakeLists.txt index 6150e60..8e8291c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,9 @@ target_include_directories(imgui PUBLIC libs/imgui/backends ) +# LibHydrogen +add_subdirectory(libs/libhydrogen EXCLUDE_FROM_ALL) + add_executable(work-calendar $<$:src/main_win32.cpp> $<$:src/main_linux.cpp> @@ -36,9 +39,10 @@ add_executable(work-calendar target_link_libraries(work-calendar PRIVATE imgui + hydrogen::hydrogen $<$:D3D11> $<$: vulkan glfw > -) \ No newline at end of file +) diff --git a/calendar.wcl b/calendar.wcl new file mode 100644 index 0000000..53a7331 Binary files /dev/null and b/calendar.wcl differ diff --git a/keks.wcl b/keks.wcl new file mode 100644 index 0000000..4bf3e9e --- /dev/null +++ b/keks.wcl @@ -0,0 +1,2 @@ +C l.HUn ".TԄ9M]$w#ƀ-x52mB*dq7UF7һe{6Q2= - + If you use this software, please cite it using the + metadata from this file. +type: software +authors: + - given-names: Frank + family-names: Denis + orcid: 'https://orcid.org/0009-0008-4417-1713' +repository-code: 'https://github.com/jedisct1/libhydrogen' +url: 'https://libhydrogen.org' +abstract: 'A lightweight, secure, easy-to-use crypto library suitable for constrained environments.' +keywords: + - cryptography + - library + - embedded +license: ISC diff --git a/libs/libhydrogen/CMakeLists.txt b/libs/libhydrogen/CMakeLists.txt new file mode 100644 index 0000000..b2a60a1 --- /dev/null +++ b/libs/libhydrogen/CMakeLists.txt @@ -0,0 +1,193 @@ +cmake_minimum_required(VERSION 3.1) + +project(hydrogen LANGUAGES C) + +include(CMakePackageConfigHelpers) +include(GNUInstallDirs) + +string(TOUPPER "${PROJECT_NAME}" setting_prefix) +function(get_setting setting_name setting_type setting_description) + string(TOUPPER "${setting_prefix}_${setting_name}" setting_external_name) + set("${setting_external_name}" "" CACHE "${setting_type}" "${setting_description}") + set("${setting_name}" "${${setting_external_name}}" PARENT_SCOPE) +endfunction() + +# Project files + +set(source_files + "${PROJECT_NAME}.c" + "impl/common.h" + "impl/core.h" + "impl/gimli-core.h" + "impl/gimli-core/portable.h" + "impl/gimli-core/sse2.h" + "impl/hash.h" + "impl/${PROJECT_NAME}_p.h" + "impl/kdf.h" + "impl/kx.h" + "impl/pwhash.h" + "impl/random.h" + "impl/secretbox.h" + "impl/sign.h" + "impl/x25519.h") + +set(header_files "${PROJECT_NAME}.h") + +set(test_files "tests/tests.c") + +set(arduino_files "library.properties") + +# Compile options + +get_setting(target_arch STRING "Target system architecture (fed to the compiler's -march=XXX).") +if(NOT target_arch AND NOT CMAKE_CROSSCOMPILING) + set(target_arch native) +endif() + +get_setting(target_device STRING "Target device identifier (defines HYDRO_TARGET_DEVICE_XXX).") + +set(compile_options + # --- GNU, Clang --- + $<$,$,$>: + # ---- Definitions ---- + $<$:-DHYDRO_TARGET_DEVICE_${target_device}> + # ---- Optimizations ---- + -Os $<$:-march=${target_arch}> -fno-exceptions + # ---- Warnings ---- + -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 + > + # --- MSVC --- + $<$: + # ---- Definitions ---- + $<$:/DHYDRO_TARGET_DEVICE_${target_device}> + # ---- Optimizations ---- + /Os /EHsc + # ---- Warnings ---- + /W4 + /wd4197 # * suppress warning "top-level volatile in cast is ignored" + /wd4146 # * suppress warning "unary minus operator applied to unsigned type, result still + # unsigned" + /wd4310 # * suppress warning "cast truncates constant value" + >) + +# Prefix project files with the project root + +# Main library + +add_library("${PROJECT_NAME}") +add_library("${PROJECT_NAME}::${PROJECT_NAME}" ALIAS "${PROJECT_NAME}") + +target_sources("${PROJECT_NAME}" PRIVATE ${source_files}) + +target_include_directories("${PROJECT_NAME}" + PUBLIC $ + $) + +target_compile_options("${PROJECT_NAME}" PRIVATE ${compile_options}) + +# Installation + +set(targets_export_name "${PROJECT_NAME}-targets") + +install(TARGETS "${PROJECT_NAME}" + EXPORT "${targets_export_name}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") + +install(FILES ${header_files} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + +# CMake find_package() support + +set(install_config_dir "${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME}") + +set(targets_export_file_name "${targets_export_name}.cmake") +set(targets_export_file "${PROJECT_BINARY_DIR}/${targets_export_file_name}") + +install(EXPORT "${targets_export_name}" + FILE "${targets_export_file_name}" + NAMESPACE "${PROJECT_NAME}::" + DESTINATION "${install_config_dir}") + +set(config_file_name "${PROJECT_NAME}-config.cmake") +set(config_template_file "${PROJECT_SOURCE_DIR}/cmake/${config_file_name}.in") +set(config_file "${PROJECT_BINARY_DIR}/${config_file_name}") + +configure_package_config_file("${config_template_file}" "${config_file}" + INSTALL_DESTINATION "${install_config_dir}") + +install(FILES "${config_file}" DESTINATION "${install_config_dir}") + +export(EXPORT "${targets_export_name}" FILE "${targets_export_file}" NAMESPACE "${PROJECT_NAME}::") + +export(PACKAGE "${PROJECT_NAME}") + +# Tests + +set(tests_executable "${PROJECT_NAME}-tests") +set(tests_run_target "${PROJECT_NAME}-run-tests") +set(tests_run_file "${PROJECT_BINARY_DIR}/${tests_run_target}.done") + +enable_testing() +add_executable("${tests_executable}" ${test_files}) +target_compile_options("${tests_executable}" PRIVATE ${compile_options}) +target_link_libraries("${tests_executable}" "${PROJECT_NAME}") +add_test(NAME "${tests_executable}" COMMAND "${tests_executable}") + +if(CMAKE_CROSSCOMPILING) + # Disable tests executable by default when cross-compiling (as it will fail to build when, e.g., + # cross-compiling for Arduino/AVR). + set_target_properties("${tests_executable}" + PROPERTIES + EXCLUDE_FROM_ALL 1 + EXCLUDE_FROM_DEFAULT_BUILD 1) +else() + # Otherwise, auto-run the tests on build. + add_custom_command(OUTPUT "${tests_run_file}" + DEPENDS "${tests_executable}" + COMMAND "${CMAKE_COMMAND}" + ARGS -E remove "${tests_run_file}" + COMMAND "${CMAKE_CTEST_COMMAND}" + ARGS -C $ --output-on-failure + COMMAND "${CMAKE_COMMAND}" + ARGS -E touch "${tests_run_file}" + WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" + VERBATIM) + add_custom_target("${tests_run_target}" ALL DEPENDS "${tests_run_file}" VERBATIM) +endif() + +# Generate Arduino package + +set(arduino_package_file "${PROJECT_BINARY_DIR}/hydrogen-crypto.zip") + +# Use the relative versions of the file path lists or else the full paths will end up in the +# generated archive. +add_custom_command(OUTPUT "${arduino_package_file}" + COMMAND "${CMAKE_COMMAND}" + ARGS -E + tar + cf + "${arduino_package_file}" + --format=zip + -- + ${source_files} + ${header_files} + ${arduino_files} + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + VERBATIM) + +add_custom_target("${PROJECT_NAME}-arduino-package" DEPENDS "${arduino_package_file}" VERBATIM) diff --git a/libs/libhydrogen/LICENSE b/libs/libhydrogen/LICENSE new file mode 100644 index 0000000..0fe3d28 --- /dev/null +++ b/libs/libhydrogen/LICENSE @@ -0,0 +1,18 @@ +/* + * ISC License + * + * Copyright (c) 2017-2026 + * Frank Denis + * + * 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. + */ diff --git a/libs/libhydrogen/Makefile b/libs/libhydrogen/Makefile new file mode 100644 index 0000000..0214611 --- /dev/null +++ b/libs/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 -mtune=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 diff --git a/libs/libhydrogen/Makefile.arduino b/libs/libhydrogen/Makefile.arduino new file mode 100644 index 0000000..daf28c2 --- /dev/null +++ b/libs/libhydrogen/Makefile.arduino @@ -0,0 +1,52 @@ +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 \ + impl/random/*.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) diff --git a/libs/libhydrogen/Makefile.chibios b/libs/libhydrogen/Makefile.chibios new file mode 100644 index 0000000..4b6f48e --- /dev/null +++ b/libs/libhydrogen/Makefile.chibios @@ -0,0 +1,43 @@ +MCU ?= cortex-m7 +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 -mcpu=$(MCU) -mthumb -mpure-code -fno-exceptions -ffunction-sections -fdata-sections -flto $(WFLAGS) +CFLAGS += -DCHIBIOS +CFLAGS += -I. +OBJ = hydrogen.o +AR ?= arm-none-eabi-ar +CC = arm-none-eabi-gcc +RANLIB ?= arm-none-eabi-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 + +lib: libhydrogen.a + +$(OBJ): $(SRC) + +libhydrogen.a: $(OBJ) + $(AR) -r $@ $^ + $(RANLIB) $@ + +.PHONY: clean + +clean: + rm -f libhydrogen.a $(OBJ) + rm -f tests/tests tests/*.done + +distclean: clean diff --git a/libs/libhydrogen/Makefile.particle b/libs/libhydrogen/Makefile.particle new file mode 100644 index 0000000..dde6454 --- /dev/null +++ b/libs/libhydrogen/Makefile.particle @@ -0,0 +1,40 @@ +PARTICLE_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: package + +package: $(PARTICLE_PACKAGE) + +$(PARTICLE_PACKAGE): + mkdir particle + mkdir particle/src + cp library.properties particle/. + cp README.md particle/. + cp LICENSE particle/. + cp -r impl particle/src/. + cp hydrogen.h particle/src/. + cp hydrogen.c particle/src/hydrogen.cpp + 7z a -tzip -mx=9 -r $(PARTICLE_PACKAGE) particle/. + rm -rf particle + +.PHONY: clean + +clean: + rm -f tests/tests + rm -rf particle + rm -f $(PARTICLE_PACKAGE) diff --git a/libs/libhydrogen/README.md b/libs/libhydrogen/README.md new file mode 100644 index 0000000..a0312ab --- /dev/null +++ b/libs/libhydrogen/README.md @@ -0,0 +1,38 @@ +[![CI](https://github.com/jedisct1/libhydrogen/actions/workflows/ci.yml/badge.svg)](https://github.com/jedisct1/libhydrogen/actions/workflows/ci.yml) +[![CodeQL scan](https://github.com/jedisct1/libhydrogen/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/jedisct1/libhydrogen/actions/workflows/codeql-analysis.yml) +[![Financial Contributors on Open Collective](https://opencollective.com/libhydrogen/all/badge.svg?label=financial+contributors)](https://opencollective.com/libhydrogen) [![Coverity Scan Build Status](https://scan.coverity.com/projects/13315/badge.svg)](https://scan.coverity.com/projects/13315) +[![TrustInSoft CI](https://ci.trust-in-soft.com/projects/jedisct1/libhydrogen.svg?branch=master)](https://ci.trust-in-soft.com/projects/jedisct1/libhydrogen) + +![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://csrc.nist.gov/CSRC/media/Projects/Lightweight-Cryptography/documents/round-1/spec-doc/gimli-spec.pdf) 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). + +## Contributors + +### Code Contributors + +This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. + diff --git a/libs/libhydrogen/build.zig b/libs/libhydrogen/build.zig new file mode 100644 index 0000000..3e95f4e --- /dev/null +++ b/libs/libhydrogen/build.zig @@ -0,0 +1,32 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseSmall }); + + const mod = b.createModule(.{ + .target = target, + .optimize = optimize, + .link_libc = true, + .strip = true, + }); + + mod.addCSourceFile(.{ + .file = b.path("hydrogen.c"), + }); + + const lib = b.addLibrary(.{ + .name = "hydrogen", + .linkage = .static, + .root_module = mod, + }); + + b.installArtifact(lib); + + _ = b.addModule("libhydrogen", .{ + .root_source_file = b.path("hydrogen.c"), + .target = target, + .optimize = optimize, + .link_libc = true, + }); +} diff --git a/libs/libhydrogen/build.zig.zon b/libs/libhydrogen/build.zig.zon new file mode 100644 index 0000000..675c443 --- /dev/null +++ b/libs/libhydrogen/build.zig.zon @@ -0,0 +1,13 @@ +.{ + .name = .libhydrogen, + .version = "1.0.0", + .fingerprint = 0x98fa0ad39dcdc390, + + .paths = .{ + "build.zig", + "build.zig.zon", + "hydrogen.c", + "hydrogen.h", + "impl", + }, +} diff --git a/libs/libhydrogen/cmake/arduino-avr-toolchain.cmake b/libs/libhydrogen/cmake/arduino-avr-toolchain.cmake new file mode 100644 index 0000000..7440b46 --- /dev/null +++ b/libs/libhydrogen/cmake/arduino-avr-toolchain.cmake @@ -0,0 +1,127 @@ +# Cross-compilation file for the Arduino/AVR toolchain. + +# To use, pass -DCMAKE_TOOLCHAIN_FILE=cmake/arduino-avr-toolchain.cmake in your CMake command line. +# You can specify the target device MCU identifier with -DHYDROGEN_ARDUINO_AVR_TARGET_DEVICE=XXX. + +cmake_minimum_required(VERSION 3.12) + +set(project_setting_prefix HYDROGEN) +function(set_project_setting setting_name setting_value) + set("${project_setting_prefix}_${setting_name}" "${setting_value}" CACHE INTERNAL "") +endfunction() + +set(setting_prefix "${project_setting_prefix}_ARDUINO_AVR") +function(get_setting setting_name setting_type setting_description) + string(TOUPPER "${setting_prefix}_${setting_name}" setting_external_name) + set("${setting_external_name}" "" CACHE "${setting_type}" "${setting_description}") + set("${setting_name}" "${${setting_external_name}}" PARENT_SCOPE) +endfunction() + +# Target device setting + +get_setting(target_device STRING "Target Arduino device MCU identifier.") +if(NOT target_device) + set(target_device atmega328p) +endif() + +if("${target_device}" STREQUAL atmega328p) + set_project_setting(TARGET_DEVICE ATMEGA328) +else() + message(FATAL_ERROR "Unrecognized ${setting_prefix}_TARGET_DEVICE value ${target_device}") +endif() + +# Find Arduino SDK home + +get_setting(sdk_dir PATH "Arduino SDK home directory.") + +# Try ARDUINO_SDK_PATH environment variable. +if(NOT sdk_dir) + if(DEFINED ENV{ARDUINO_SDK_PATH}) + set(sdk_dir "$ENV{ARDUINO_SDK_PATH}") + endif() +endif() + +# Try some platform-specific guesses. +if(NOT sdk_dir) + # Windows + if(WIN32) + list(APPEND arduino_home_dir_guesses "C:/Program Files (x86)/Arduino" + "C:/Program Files/Arduino") + endif() + + # macOS + if(APPLE) + list(APPEND arduino_home_dir_guesses "/Applications/Arduino.app/Contents/Java") + endif() + + # Linux/Unix + if(UNIX AND NOT APPLE) + list(APPEND arduino_home_dir_guesses "/usr/share/arduino" "/usr/local/share/arduino") + endif() + + if(DEFINED arduino_home_dir_guesses) + foreach(arduino_home_dir_guess IN LISTS arduino_home_dir_guesses) + if(IS_DIRECTORY "${arduino_home_dir_guess}") + set(sdk_dir "${arduino_home_dir_guess}") + break() + endif() + endforeach() + endif() +endif() + +if(NOT sdk_dir) + message(FATAL_ERROR "Couldn't determine Arduino SDK home directory. " + "Try passing -D${setting_prefix}_SDK_DIR=... to the CMake command line, or " + "set the ARDUINO_SDK_PATH environment variable.") +endif() + +# Locate toolchain programs + +set(arduino_tools_dir "${sdk_dir}/hardware/tools/avr/bin") +set(program_prefix "${setting_prefix}_PROGRAM") +function(find_in_toolchain program_name) + string(TOUPPER "${program_prefix}_${program_name}" program_external_name) + string(REPLACE "_" "-" program_file_name "${program_name}") + + find_program("${program_external_name}" "${program_file_name}" + PATHS "${arduino_tools_dir}" + NO_DEFAULT_PATH) + + if("${${program_external_name}}" STREQUAL "${program_external_name}-NOTFOUND") + message(FATAL_ERROR "Couldn't find program ${program_file_name} " + "in Arduino/AVR toolchain at ${arduino_tools_dir}") + else() + set("${program_name}" "${${program_external_name}}" PARENT_SCOPE) + endif() +endfunction() + +find_in_toolchain(avr_gcc) +find_in_toolchain(avr_gcc_ranlib) +find_in_toolchain(avr_gcc_ar) +find_in_toolchain(avr_gcc_nm) +find_in_toolchain(avr_strip) + +# Configure CMake toolchain settings + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_C_COMPILER "${avr_gcc}") +set(CMAKE_ASM_COMPILER "${avr_gcc}") +set(CMAKE_RANLIB "${avr_gcc_ranlib}") +set(CMAKE_AR "${avr_gcc_ar}") +set(CMAKE_NM "${avr_gcc_nm}") +set(CMAKE_STRIP "${avr_strip}") + +set(CMAKE_C_OUTPUT_EXTENSION .o) +set(CMAKE_ASM_OUTPUT_EXTENSION .o) + +# Set compile flags + +string(CONCAT CMAKE_C_FLAGS " -mmcu=${target_device} -mcall-prologues -fno-exceptions" + " -ffunction-sections -fdata-sections -flto") + +# Add include directories + +include_directories(SYSTEM "${sdk_dir}/hardware/arduino/avr/cores/arduino" + "${sdk_dir}/hardware/arduino/avr/variants/standard" + "${sdk_dir}/hardware/arduino/cores/arduino" + "${sdk_dir}/hardware/arduino/variants/standard") diff --git a/libs/libhydrogen/cmake/hydrogen-config.cmake.in b/libs/libhydrogen/cmake/hydrogen-config.cmake.in new file mode 100644 index 0000000..d374391 --- /dev/null +++ b/libs/libhydrogen/cmake/hydrogen-config.cmake.in @@ -0,0 +1,7 @@ +get_filename_component(hydrogen_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) + +if(NOT TARGET hydrogen::hydrogen) + include("${hydrogen_CMAKE_DIR}/hydrogen-targets.cmake") +endif() + +set(hydrogen_LIBRARIES hydrogen::hydrogen) diff --git a/libs/libhydrogen/cmake/wasm32-wasi-toolchain.cmake b/libs/libhydrogen/cmake/wasm32-wasi-toolchain.cmake new file mode 100644 index 0000000..cf46cd3 --- /dev/null +++ b/libs/libhydrogen/cmake/wasm32-wasi-toolchain.cmake @@ -0,0 +1,58 @@ +# Cross-compilation file for WebAssembly with WASI. + +# To use, pass -DCMAKE_TOOLCHAIN_FILE=cmake/wasm32-wasi-toolchain.cmake in your CMake command line. +# You'll also need to specify a sysroot directory with -DHYDROGEN_WASM32_WASI_SYSROOT_DIR=XXX. + +cmake_minimum_required(VERSION 3.12) + +set(project_setting_prefix HYDROGEN) +set(setting_prefix "${project_setting_prefix}_WASM32_WASI") +function(get_setting setting_name setting_type setting_description) + string(TOUPPER "${setting_prefix}_${setting_name}" setting_external_name) + set("${setting_external_name}" "" CACHE "${setting_type}" "${setting_description}") + set("${setting_name}" "${${setting_external_name}}" PARENT_SCOPE) +endfunction() + +# Sysroot setting + +get_setting(sysroot_dir STRING "Directory containing the wasm32-wasi sysroot.") + +# Locate toolchain programs + +set(program_prefix "${setting_prefix}_PROGRAM") +function(find_in_toolchain program_name) + string(TOUPPER "${program_prefix}_${program_name}" program_external_name) + string(REPLACE "_" "-" program_file_name "${program_name}") + + find_program("${program_external_name}" "${program_file_name}") + + if("${${program_external_name}}" STREQUAL "${program_external_name}-NOTFOUND") + message(FATAL_ERROR "Couldn't find toolchain program ${program_file_name}") + else() + set("${program_name}" "${${program_external_name}}" PARENT_SCOPE) + endif() +endfunction() + +find_in_toolchain(clang) +find_in_toolchain(llvm_ranlib) +find_in_toolchain(llvm_ar) +find_in_toolchain(llvm_nm) +find_in_toolchain(llvm_strip) + +# Configure CMake toolchain settings + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_C_COMPILER "${clang}") +set(CMAKE_ASM_COMPILER "${clang}") +set(CMAKE_RANLIB "${llvm_ranlib}") +set(CMAKE_AR "${llvm_ar}") +set(CMAKE_NM "${llvm_nm}") +set(CMAKE_STRIP "${llvm_strip}") + +set(CMAKE_C_OUTPUT_EXTENSION .o) +set(CMAKE_ASM_OUTPUT_EXTENSION .o) + +# Set compile flags + +string(CONCAT CMAKE_C_FLAGS " -DED25519_NONDETERMINISTIC=1 --target=wasm32-wasi" + " --sysroot=${sysroot_dir} -Wl,--stack-first") diff --git a/libs/libhydrogen/hydrogen.c b/libs/libhydrogen/hydrogen.c new file mode 100644 index 0000000..c3e7127 --- /dev/null +++ b/libs/libhydrogen/hydrogen.c @@ -0,0 +1,19 @@ +#include "hydrogen.h" + +#include "impl/common.h" +#include "impl/hydrogen_p.h" + +#include "impl/random.h" + +#include "impl/core.h" +#include "impl/gimli-core.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" diff --git a/libs/libhydrogen/hydrogen.h b/libs/libhydrogen/hydrogen.h new file mode 100644 index 0000000..32de848 --- /dev/null +++ b/libs/libhydrogen/hydrogen.h @@ -0,0 +1,337 @@ +#ifndef hydrogen_H +#define hydrogen_H + +#if !(defined(__linux__) && defined(__KERNEL__)) +# include +# include +# include +#endif + +#if !defined(__cplusplus) && defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wdeclaration-after-statement" +#endif + +#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; + hydro_hash_state h_st; +} 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 + 16) + +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 + 16) +#define hydro_kx_KK_PACKET2BYTES (32 + 16) + +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 + 16) +#define hydro_kx_XX_PACKET2BYTES (32 + 32 + 16 + 16) +#define hydro_kx_XX_PACKET3BYTES (32 + 16 + 16) + +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]); + +/* NOISE_NK */ + +#define hydro_kx_NK_PACKET1BYTES (32 + 16) +#define hydro_kx_NK_PACKET2BYTES (32 + 16) + +int hydro_kx_nk_1(hydro_kx_state *state, uint8_t packet1[hydro_kx_NK_PACKET1BYTES], + const uint8_t psk[hydro_kx_PSKBYTES], + const uint8_t peer_static_pk[hydro_kx_PUBLICKEYBYTES]); + +int hydro_kx_nk_2(hydro_kx_session_keypair *kp, uint8_t packet2[hydro_kx_NK_PACKET2BYTES], + const uint8_t packet1[hydro_kx_NK_PACKET1BYTES], + const uint8_t psk[hydro_kx_PSKBYTES], const hydro_kx_keypair *static_kp); + +int hydro_kx_nk_3(hydro_kx_state *state, hydro_kx_session_keypair *kp, + const uint8_t packet2[hydro_kx_NK_PACKET2BYTES]); + +/* ---------------- */ + +#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 diff --git a/libs/libhydrogen/impl/common.h b/libs/libhydrogen/impl/common.h new file mode 100644 index 0000000..44175d1 --- /dev/null +++ b/libs/libhydrogen/impl/common.h @@ -0,0 +1,334 @@ +#if defined(__linux__) && defined(__KERNEL__) +# define TLS /* Danger: at most one call into hydro_*() at a time */ +# define CHAR_BIT 8 +# define abort BUG +# define uint_fast16_t uint16_t +# define errno hydro_errno +static int errno; +#else +# include +# include +# include +# include +# include +# include +#endif + +#if defined (__CHERIOT__) +static int errno; +#endif + +#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__) && !defined(__TINYC__) +# 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 }; diff --git a/libs/libhydrogen/impl/core.h b/libs/libhydrogen/impl/core.h new file mode 100644 index 0000000..0498ee5 --- /dev/null +++ b/libs/libhydrogen/impl/core.h @@ -0,0 +1,221 @@ +int +hydro_init(void) +{ + hydro_random_ensure_initialized(); + 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_t) 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 & (1U + ~is_barrier); + valid |= (unsigned char) is_barrier; + } + if (valid == 0) { + return -1; + } + return (int) (padded_buflen - 1 - pad_len); +} diff --git a/libs/libhydrogen/impl/gimli-core.h b/libs/libhydrogen/impl/gimli-core.h new file mode 100644 index 0000000..83ee82a --- /dev/null +++ b/libs/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 +} diff --git a/libs/libhydrogen/impl/gimli-core/portable.h b/libs/libhydrogen/impl/gimli-core/portable.h new file mode 100644 index 0000000..db6b84c --- /dev/null +++ b/libs/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; + } + } +} diff --git a/libs/libhydrogen/impl/gimli-core/sse2.h b/libs/libhydrogen/impl/gimli-core/sse2.h new file mode 100644 index 0000000..0e6fce6 --- /dev/null +++ b/libs/libhydrogen/impl/gimli-core/sse2.h @@ -0,0 +1,112 @@ +#include +#ifdef __SSSE3__ +# include +#endif + +#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 _hydro_attr_aligned_(16) coeffs[24] = { + 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); +} diff --git a/libs/libhydrogen/impl/hash.h b/libs/libhydrogen/impl/hash.h new file mode 100644 index 0000000..209ca28 --- /dev/null +++ b/libs/libhydrogen/impl/hash.h @@ -0,0 +1,142 @@ +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) { + left = gimli_RATE - state->buf_off; + if ((ps = in_len) > left) { + ps = left; + } + for (i = 0; i < ps; i++) { + buf[state->buf_off + i] ^= in[i]; + } + in += ps; + in_len -= ps; + state->buf_off += (uint8_t) ps; + if (state->buf_off == gimli_RATE) { + gimli_core_u8(buf, 0); + state->buf_off = 0; + } + } + 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 == 0) { + /* allow callers to finalize without producing output */ + } else if (out_len < hydro_hash_BYTES_MIN || out_len > hydro_hash_BYTES_MAX || out == NULL) { + 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); + } + state->buf_off = gimli_RATE; + + 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); +} diff --git a/libs/libhydrogen/impl/hydrogen_p.h b/libs/libhydrogen/impl/hydrogen_p.h new file mode 100644 index 0000000..f228fa2 --- /dev/null +++ b/libs/libhydrogen/impl/hydrogen_p.h @@ -0,0 +1,82 @@ +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_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_SECRETKEYBYTES], + const uint8_t x1[hydro_x25519_PUBLICKEYBYTES], + 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]); diff --git a/libs/libhydrogen/impl/kdf.h b/libs/libhydrogen/impl/kdf.h new file mode 100644 index 0000000..22a222c --- /dev/null +++ b/libs/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); +} diff --git a/libs/libhydrogen/impl/kx.h b/libs/libhydrogen/impl/kx.h new file mode 100644 index 0000000..97f9453 --- /dev/null +++ b/libs/libhydrogen/impl/kx.h @@ -0,0 +1,535 @@ +#define hydro_kx_AEAD_KEYBYTES hydro_hash_KEYBYTES +#define hydro_kx_AEAD_MACBYTES 16 + +#define hydro_kx_CONTEXT "hydro_kx" + +static void +hydro_kx_aead_init(uint8_t aead_state[gimli_BLOCKBYTES], uint8_t k[hydro_kx_AEAD_KEYBYTES], + hydro_kx_state *state) +{ + static const uint8_t prefix[] = { 6, 'k', 'x', 'x', '2', '5', '6', 0 }; + + hydro_hash_final(&state->h_st, k, hydro_kx_AEAD_KEYBYTES); + + mem_zero(aead_state + sizeof prefix, gimli_BLOCKBYTES - sizeof prefix); + memcpy(aead_state, prefix, sizeof prefix); + gimli_core_u8(aead_state, gimli_TAG_HEADER); + + COMPILER_ASSERT(hydro_kx_AEAD_KEYBYTES == 2 * gimli_RATE); + mem_xor(aead_state, k, gimli_RATE); + gimli_core_u8(aead_state, gimli_TAG_KEY); + mem_xor(aead_state, k + gimli_RATE, gimli_RATE); + gimli_core_u8(aead_state, gimli_TAG_KEY); +} + +static void +hydro_kx_aead_final(uint8_t *aead_state, const uint8_t key[hydro_kx_AEAD_KEYBYTES]) +{ + COMPILER_ASSERT(hydro_kx_AEAD_KEYBYTES == gimli_CAPACITY); + mem_xor(aead_state + gimli_RATE, key, hydro_kx_AEAD_KEYBYTES); + gimli_core_u8(aead_state, gimli_TAG_FINAL); + mem_xor(aead_state + gimli_RATE, key, hydro_kx_AEAD_KEYBYTES); + gimli_core_u8(aead_state, gimli_TAG_FINAL); +} + +static void +hydro_kx_aead_xor_enc(uint8_t aead_state[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], aead_state, gimli_RATE); + memcpy(aead_state, &out[i * gimli_RATE], gimli_RATE); + gimli_core_u8(aead_state, gimli_TAG_PAYLOAD); + } + leftover = inlen % gimli_RATE; + if (leftover != 0) { + mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], aead_state, leftover); + mem_cpy(aead_state, &out[i * gimli_RATE], leftover); + } + gimli_pad_u8(aead_state, leftover, gimli_DOMAIN_AEAD); + gimli_core_u8(aead_state, gimli_TAG_PAYLOAD); +} + +static void +hydro_kx_aead_xor_dec(uint8_t aead_state[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], aead_state, gimli_RATE); + memcpy(aead_state, &in[i * gimli_RATE], gimli_RATE); + gimli_core_u8(aead_state, gimli_TAG_PAYLOAD); + } + leftover = inlen % gimli_RATE; + if (leftover != 0) { + mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], aead_state, leftover); + mem_cpy(aead_state, &in[i * gimli_RATE], leftover); + } + gimli_pad_u8(aead_state, leftover, gimli_DOMAIN_AEAD); + gimli_core_u8(aead_state, gimli_TAG_PAYLOAD); +} + +static void +hydro_kx_aead_encrypt(hydro_kx_state *state, uint8_t *c, const uint8_t *m, size_t mlen) +{ + _hydro_attr_aligned_(16) uint8_t aead_state[gimli_BLOCKBYTES]; + uint8_t k[hydro_kx_AEAD_KEYBYTES]; + uint8_t *mac = &c[0]; + uint8_t *ct = &c[hydro_kx_AEAD_MACBYTES]; + + hydro_kx_aead_init(aead_state, k, state); + hydro_kx_aead_xor_enc(aead_state, ct, m, mlen); + hydro_kx_aead_final(aead_state, k); + COMPILER_ASSERT(hydro_kx_AEAD_MACBYTES <= gimli_CAPACITY); + memcpy(mac, aead_state + gimli_RATE, hydro_kx_AEAD_MACBYTES); + hydro_hash_update(&state->h_st, c, mlen + hydro_kx_AEAD_MACBYTES); +} + +static int hydro_kx_aead_decrypt(hydro_kx_state *state, uint8_t *m, const uint8_t *c, + size_t clen) _hydro_attr_warn_unused_result_; + +static int +hydro_kx_aead_decrypt(hydro_kx_state *state, uint8_t *m, const uint8_t *c, size_t clen) +{ + _hydro_attr_aligned_(16) uint32_t int_state[gimli_BLOCKBYTES / 4]; + uint32_t pub_mac[hydro_kx_AEAD_MACBYTES / 4]; + uint8_t k[hydro_kx_AEAD_KEYBYTES]; + uint8_t *aead_state = (uint8_t *) (void *) int_state; + const uint8_t *mac; + const uint8_t *ct; + size_t mlen; + uint32_t cv; + + if (clen < hydro_kx_AEAD_MACBYTES) { + return -1; + } + mac = &c[0]; + ct = &c[hydro_kx_AEAD_MACBYTES]; + mlen = clen - hydro_kx_AEAD_MACBYTES; + memcpy(pub_mac, mac, sizeof pub_mac); + hydro_kx_aead_init(aead_state, k, state); + hydro_hash_update(&state->h_st, c, clen); + hydro_kx_aead_xor_dec(aead_state, m, ct, mlen); + hydro_kx_aead_final(aead_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; +} + +/* -- */ + +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_init_state(hydro_kx_state *state, const char *name) +{ + mem_zero(state, sizeof *state); + hydro_hash_init(&state->h_st, hydro_kx_CONTEXT, NULL); + hydro_hash_update(&state->h_st, name, strlen(name)); + hydro_hash_final(&state->h_st, NULL, 0); +} + +static void +hydro_kx_final(hydro_kx_state *state, uint8_t session_k1[hydro_kx_SESSIONKEYBYTES], + uint8_t session_k2[hydro_kx_SESSIONKEYBYTES]) +{ + uint8_t kdf_key[hydro_kdf_KEYBYTES]; + + hydro_hash_final(&state->h_st, kdf_key, sizeof kdf_key); + hydro_kdf_derive_from_key(session_k1, hydro_kx_SESSIONKEYBYTES, 0, hydro_kx_CONTEXT, kdf_key); + hydro_kdf_derive_from_key(session_k2, hydro_kx_SESSIONKEYBYTES, 1, hydro_kx_CONTEXT, kdf_key); +} + +static int +hydro_kx_dh(hydro_kx_state *state, const uint8_t sk[hydro_x25519_SECRETKEYBYTES], + const uint8_t pk[hydro_x25519_PUBLICKEYBYTES]) +{ + uint8_t dh_result[hydro_x25519_BYTES]; + + if (hydro_x25519_scalarmult(dh_result, sk, pk, 1) != 0) { + return -1; + } + hydro_hash_update(&state->h_st, dh_result, hydro_x25519_BYTES); + + return 0; +} + +static void +hydro_kx_eph_keygen(hydro_kx_state *state, hydro_kx_keypair *kp) +{ + hydro_kx_keygen(kp); + hydro_hash_update(&state->h_st, kp->pk, sizeof kp->pk); +} + +/* 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 *packet1_eph_pk = &packet1[0]; + uint8_t *packet1_mac = &packet1[hydro_kx_PUBLICKEYBYTES]; + + if (psk == NULL) { + psk = zero; + } + hydro_kx_init_state(&state, "Noise_Npsk0_hydro1"); + hydro_hash_update(&state.h_st, peer_static_pk, hydro_x25519_PUBLICKEYBYTES); + + hydro_hash_update(&state.h_st, psk, hydro_kx_PSKBYTES); + hydro_kx_eph_keygen(&state, &state.eph_kp); + if (hydro_kx_dh(&state, state.eph_kp.sk, peer_static_pk) != 0) { + return -1; + } + hydro_kx_aead_encrypt(&state, packet1_mac, NULL, 0); + memcpy(packet1_eph_pk, state.eph_kp.pk, sizeof state.eph_kp.pk); + + 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; + const uint8_t *peer_eph_pk = &packet1[0]; + const uint8_t *packet1_mac = &packet1[hydro_kx_PUBLICKEYBYTES]; + + if (psk == NULL) { + psk = zero; + } + hydro_kx_init_state(&state, "Noise_Npsk0_hydro1"); + hydro_hash_update(&state.h_st, static_kp->pk, hydro_kx_PUBLICKEYBYTES); + + hydro_hash_update(&state.h_st, psk, hydro_kx_PSKBYTES); + hydro_hash_update(&state.h_st, peer_eph_pk, hydro_x25519_PUBLICKEYBYTES); + if (hydro_kx_dh(&state, static_kp->sk, peer_eph_pk) != 0 || + hydro_kx_aead_decrypt(&state, NULL, packet1_mac, hydro_kx_AEAD_MACBYTES) != 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 *packet1_eph_pk = &packet1[0]; + uint8_t *packet1_mac = &packet1[hydro_kx_PUBLICKEYBYTES]; + + hydro_kx_init_state(state, "Noise_KK_hydro1"); + hydro_hash_update(&state->h_st, static_kp->pk, hydro_kx_PUBLICKEYBYTES); + hydro_hash_update(&state->h_st, peer_static_pk, hydro_kx_PUBLICKEYBYTES); + + hydro_kx_eph_keygen(state, &state->eph_kp); + if (hydro_kx_dh(state, state->eph_kp.sk, peer_static_pk) != 0 || + hydro_kx_dh(state, static_kp->sk, peer_static_pk) != 0) { + return -1; + } + hydro_kx_aead_encrypt(state, packet1_mac, NULL, 0); + memcpy(packet1_eph_pk, state->eph_kp.pk, sizeof state->eph_kp.pk); + + 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; + const uint8_t *peer_eph_pk = &packet1[0]; + const uint8_t *packet1_mac = &packet1[hydro_kx_PUBLICKEYBYTES]; + uint8_t *packet2_eph_pk = &packet2[0]; + uint8_t *packet2_mac = &packet2[hydro_kx_PUBLICKEYBYTES]; + + hydro_kx_init_state(&state, "Noise_KK_hydro1"); + hydro_hash_update(&state.h_st, peer_static_pk, hydro_kx_PUBLICKEYBYTES); + hydro_hash_update(&state.h_st, static_kp->pk, hydro_kx_PUBLICKEYBYTES); + + hydro_hash_update(&state.h_st, peer_eph_pk, hydro_kx_PUBLICKEYBYTES); + if (hydro_kx_dh(&state, static_kp->sk, peer_eph_pk) != 0 || + hydro_kx_dh(&state, static_kp->sk, peer_static_pk) != 0 || + hydro_kx_aead_decrypt(&state, NULL, packet1_mac, hydro_kx_AEAD_MACBYTES) != 0) { + return -1; + } + + hydro_kx_eph_keygen(&state, &state.eph_kp); + if (hydro_kx_dh(&state, state.eph_kp.sk, peer_eph_pk) != 0 || + hydro_kx_dh(&state, state.eph_kp.sk, peer_static_pk) != 0) { + return -1; + } + hydro_kx_aead_encrypt(&state, packet2_mac, NULL, 0); + hydro_kx_final(&state, kp->tx, kp->rx); + memcpy(packet2_eph_pk, state.eph_kp.pk, sizeof state.eph_kp.pk); + + 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) +{ + const uint8_t *peer_eph_pk = packet2; + const uint8_t *packet2_mac = &packet2[hydro_kx_PUBLICKEYBYTES]; + + hydro_hash_update(&state->h_st, peer_eph_pk, hydro_kx_PUBLICKEYBYTES); + if (hydro_kx_dh(state, state->eph_kp.sk, peer_eph_pk) != 0 || + hydro_kx_dh(state, static_kp->sk, peer_eph_pk) != 0) { + return -1; + } + + if (hydro_kx_aead_decrypt(state, NULL, packet2_mac, hydro_kx_AEAD_MACBYTES) != 0) { + return -1; + } + hydro_kx_final(state, kp->rx, kp->tx); + + 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]) +{ + uint8_t *packet1_eph_pk = &packet1[0]; + uint8_t *packet1_mac = &packet1[hydro_kx_PUBLICKEYBYTES]; + + if (psk == NULL) { + psk = zero; + } + hydro_kx_init_state(state, "Noise_XXpsk0+psk3_hydro1"); + + hydro_kx_eph_keygen(state, &state->eph_kp); + hydro_hash_update(&state->h_st, psk, hydro_kx_PSKBYTES); + memcpy(packet1_eph_pk, state->eph_kp.pk, sizeof state->eph_kp.pk); + hydro_kx_aead_encrypt(state, packet1_mac, NULL, 0); + + 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) +{ + const uint8_t *peer_eph_pk = &packet1[0]; + const uint8_t *packet1_mac = &packet1[hydro_kx_PUBLICKEYBYTES]; + uint8_t *packet2_eph_pk = &packet2[0]; + uint8_t *packet2_enc_static_pk = &packet2[hydro_kx_PUBLICKEYBYTES]; + uint8_t *packet2_mac = + &packet2[hydro_kx_PUBLICKEYBYTES + hydro_kx_PUBLICKEYBYTES + hydro_kx_AEAD_MACBYTES]; + + if (psk == NULL) { + psk = zero; + } + hydro_kx_init_state(state, "Noise_XXpsk0+psk3_hydro1"); + + hydro_hash_update(&state->h_st, peer_eph_pk, hydro_kx_PUBLICKEYBYTES); + hydro_hash_update(&state->h_st, psk, hydro_kx_PSKBYTES); + if (hydro_kx_aead_decrypt(state, NULL, packet1_mac, hydro_kx_AEAD_MACBYTES) != 0) { + return -1; + } + + hydro_kx_eph_keygen(state, &state->eph_kp); + if (hydro_kx_dh(state, state->eph_kp.sk, peer_eph_pk) != 0) { + return -1; + } + hydro_kx_aead_encrypt(state, packet2_enc_static_pk, static_kp->pk, sizeof static_kp->pk); + if (hydro_kx_dh(state, static_kp->sk, peer_eph_pk) != 0) { + return -1; + } + hydro_kx_aead_encrypt(state, packet2_mac, NULL, 0); + + memcpy(packet2_eph_pk, state->eph_kp.pk, sizeof state->eph_kp.pk); + + 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 peer_static_pk_[hydro_kx_PUBLICKEYBYTES]; + const uint8_t *peer_eph_pk = &packet2[0]; + const uint8_t *peer_enc_static_pk = &packet2[hydro_kx_PUBLICKEYBYTES]; + const uint8_t *packet2_mac = + &packet2[hydro_kx_PUBLICKEYBYTES + hydro_kx_PUBLICKEYBYTES + hydro_kx_AEAD_MACBYTES]; + uint8_t *packet3_enc_static_pk = &packet3[0]; + uint8_t *packet3_mac = &packet3[hydro_kx_PUBLICKEYBYTES + hydro_kx_AEAD_MACBYTES]; + + if (psk == NULL) { + psk = zero; + } + if (peer_static_pk == NULL) { + peer_static_pk = peer_static_pk_; + } + hydro_hash_update(&state->h_st, peer_eph_pk, hydro_kx_PUBLICKEYBYTES); + if (hydro_kx_dh(state, state->eph_kp.sk, peer_eph_pk) != 0 || + hydro_kx_aead_decrypt(state, peer_static_pk, peer_enc_static_pk, + hydro_kx_PUBLICKEYBYTES + hydro_kx_AEAD_MACBYTES) != 0 || + hydro_kx_dh(state, state->eph_kp.sk, peer_static_pk) != 0 || + hydro_kx_aead_decrypt(state, NULL, packet2_mac, hydro_kx_AEAD_MACBYTES) != 0) { + return -1; + } + + hydro_kx_aead_encrypt(state, packet3_enc_static_pk, static_kp->pk, sizeof static_kp->pk); + if (hydro_kx_dh(state, static_kp->sk, peer_eph_pk) != 0) { + return -1; + } + hydro_hash_update(&state->h_st, psk, hydro_kx_PSKBYTES); + hydro_kx_aead_encrypt(state, packet3_mac, NULL, 0); + 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 peer_static_pk_[hydro_kx_PUBLICKEYBYTES]; + const uint8_t *peer_enc_static_pk = &packet3[0]; + const uint8_t *packet3_mac = &packet3[hydro_kx_PUBLICKEYBYTES + hydro_kx_AEAD_MACBYTES]; + + if (psk == NULL) { + psk = zero; + } + if (peer_static_pk == NULL) { + peer_static_pk = peer_static_pk_; + } + if (hydro_kx_aead_decrypt(state, peer_static_pk, peer_enc_static_pk, + hydro_kx_PUBLICKEYBYTES + hydro_kx_AEAD_MACBYTES) != 0 || + hydro_kx_dh(state, state->eph_kp.sk, peer_static_pk) != 0) { + return -1; + } + hydro_hash_update(&state->h_st, psk, hydro_kx_PSKBYTES); + if (hydro_kx_aead_decrypt(state, NULL, packet3_mac, hydro_kx_AEAD_MACBYTES) != 0) { + return -1; + } + hydro_kx_final(state, kp->tx, kp->rx); + + return 0; +} + +/* NOISE_NK */ + +int +hydro_kx_nk_1(hydro_kx_state *state, uint8_t packet1[hydro_kx_NK_PACKET1BYTES], + const uint8_t psk[hydro_kx_PSKBYTES], + const uint8_t peer_static_pk[hydro_kx_PUBLICKEYBYTES]) +{ + uint8_t *packet1_eph_pk = &packet1[0]; + uint8_t *packet1_mac = &packet1[hydro_kx_PUBLICKEYBYTES]; + + if (psk == NULL) { + psk = zero; + } + hydro_kx_init_state(state, "Noise_NKpsk0_hydro1"); + hydro_hash_update(&state->h_st, peer_static_pk, hydro_x25519_PUBLICKEYBYTES); + + hydro_hash_update(&state->h_st, psk, hydro_kx_PSKBYTES); + hydro_kx_eph_keygen(state, &state->eph_kp); + if (hydro_kx_dh(state, state->eph_kp.sk, peer_static_pk) != 0) { + return -1; + } + hydro_kx_aead_encrypt(state, packet1_mac, NULL, 0); + memcpy(packet1_eph_pk, state->eph_kp.pk, sizeof state->eph_kp.pk); + + return 0; +} + +int +hydro_kx_nk_2(hydro_kx_session_keypair *kp, uint8_t packet2[hydro_kx_NK_PACKET2BYTES], + const uint8_t packet1[hydro_kx_NK_PACKET1BYTES], const uint8_t psk[hydro_kx_PSKBYTES], + const hydro_kx_keypair *static_kp) +{ + hydro_kx_state state; + const uint8_t *peer_eph_pk = &packet1[0]; + const uint8_t *packet1_mac = &packet1[hydro_kx_PUBLICKEYBYTES]; + uint8_t *packet2_eph_pk = &packet2[0]; + uint8_t *packet2_mac = &packet2[hydro_kx_PUBLICKEYBYTES]; + + if (psk == NULL) { + psk = zero; + } + hydro_kx_init_state(&state, "Noise_NKpsk0_hydro1"); + hydro_hash_update(&state.h_st, static_kp->pk, hydro_kx_PUBLICKEYBYTES); + + hydro_hash_update(&state.h_st, psk, hydro_kx_PSKBYTES); + hydro_hash_update(&state.h_st, peer_eph_pk, hydro_x25519_PUBLICKEYBYTES); + if (hydro_kx_dh(&state, static_kp->sk, peer_eph_pk) != 0 || + hydro_kx_aead_decrypt(&state, NULL, packet1_mac, hydro_kx_AEAD_MACBYTES) != 0) { + return -1; + } + + hydro_kx_eph_keygen(&state, &state.eph_kp); + if (hydro_kx_dh(&state, state.eph_kp.sk, peer_eph_pk) != 0) { + return -1; + } + hydro_kx_aead_encrypt(&state, packet2_mac, NULL, 0); + hydro_kx_final(&state, kp->tx, kp->rx); + memcpy(packet2_eph_pk, state.eph_kp.pk, sizeof state.eph_kp.pk); + + return 0; +} + +int +hydro_kx_nk_3(hydro_kx_state *state, hydro_kx_session_keypair *kp, + const uint8_t packet2[hydro_kx_NK_PACKET2BYTES]) +{ + const uint8_t *peer_eph_pk = &packet2[0]; + const uint8_t *packet2_mac = &packet2[hydro_kx_PUBLICKEYBYTES]; + + hydro_hash_update(&state->h_st, peer_eph_pk, hydro_x25519_PUBLICKEYBYTES); + if (hydro_kx_dh(state, state->eph_kp.sk, peer_eph_pk) != 0 || + hydro_kx_aead_decrypt(state, NULL, packet2_mac, hydro_kx_AEAD_MACBYTES) != 0) { + return -1; + } + hydro_kx_final(state, kp->rx, kp->tx); + + return 0; +} diff --git a/libs/libhydrogen/impl/pwhash.h b/libs/libhydrogen/impl/pwhash.h new file mode 100644 index 0000000..2598f47 --- /dev/null +++ b/libs/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 = ¶ms[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 = ¶ms[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); +} diff --git a/libs/libhydrogen/impl/random.h b/libs/libhydrogen/impl/random.h new file mode 100644 index 0000000..303af7c --- /dev/null +++ b/libs/libhydrogen/impl/random.h @@ -0,0 +1,162 @@ +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 "random/avr.h" +#elif (defined(ESP32) || defined(ESP8266)) && !defined(__unix__) +# include "random/esp32.h" +#elif defined(PARTICLE) && defined(PLATFORM_ID) && PLATFORM_ID > 2 && !defined(__unix__) +# include "random/particle.h" +#elif defined(__ZEPHYR__) +# include "random/zephyr.h" +#elif (defined(NRF52832_XXAA) || defined(NRF52832_XXAB)) && !defined(__unix__) +# include "random/nrf52832.h" +#elif defined(_WIN32) +# include "random/windows.h" +#elif defined(__wasi__) +# include "random/wasi.h" +#elif defined(__linux__) && defined(__KERNEL__) +# include "random/linux_kernel.h" +#elif defined(__unix__) +# include "random/unix.h" +#elif defined(TARGET_LIKE_MBED) +# include "random/mbed.h" +#elif defined(RIOT_VERSION) +# include "random/riot.h" +#elif defined(STM32F4) || defined(STM32L4) +# include "random/stm32.h" +#elif defined(__RTTHREAD__) +# include "random/rtthread.h" +#elif defined(CH32V30x_D8) || defined(CH32V30x_D8C) +# include "random/ch32.h" +#elif defined(CHIBIOS) +# include "random/chibios.h" +#elif defined(__CHERIOT__) +# include "random/cheriot.h" +#elif defined(PICO_BUILD) +# ifndef LIB_PICO_RAND +# error pico-sdk detected but pico_rand not configured +# endif +# include "random/pico-sdk.h" +#else +# error Unsupported platform +#endif + +static void +hydro_random_ensure_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_ensure_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_ensure_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); + } +} + +void +hydro_random_reseed(void) +{ + hydro_random_context.initialized = 0; + hydro_random_ensure_initialized(); +} diff --git a/libs/libhydrogen/impl/random/avr.h b/libs/libhydrogen/impl/random/avr.h new file mode 100644 index 0000000..a02ebc2 --- /dev/null +++ b/libs/libhydrogen/impl/random/avr.h @@ -0,0 +1,63 @@ +#include + +static bool +hydro_random_rbit(uint16_t x) +{ + uint8_t x8; + + x8 = ((uint8_t) (x >> 8)) ^ (uint8_t) x; + x8 = (x8 >> 4) ^ (x8 & 0xf); + x8 = (x8 >> 2) ^ (x8 & 0x3); + x8 = (x8 >> 1) ^ x8; + + return (bool) (x8 & 1); +} + +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) +{ +} diff --git a/libs/libhydrogen/impl/random/ch32.h b/libs/libhydrogen/impl/random/ch32.h new file mode 100644 index 0000000..263579c --- /dev/null +++ b/libs/libhydrogen/impl/random/ch32.h @@ -0,0 +1,35 @@ +#if defined(CH32V30x_D8) || defined(CH32V30x_D8C) +# include +#else +# error CH32 implementation missing! +#endif + +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; + + // Enable RNG clock source + RCC_AHBPeriphClockCmd(RCC_AHBPeriph_RNG, ENABLE); + + // RNG Peripheral enable + RNG_Cmd(ENABLE); + + hydro_hash_init(&st, ctx, NULL); + + while (ebits < 256) { + while (RNG_GetFlagStatus(RNG_FLAG_DRDY) == RESET) + ; + uint32_t r = RNG_GetRandomNumber(); + + 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; +} diff --git a/libs/libhydrogen/impl/random/cheriot.h b/libs/libhydrogen/impl/random/cheriot.h new file mode 100644 index 0000000..298a79f --- /dev/null +++ b/libs/libhydrogen/impl/random/cheriot.h @@ -0,0 +1,22 @@ +uint32_t rand_32(); + +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; + + hydro_hash_init(&st, ctx, NULL); + + while (ebits < 256) { + uint32_t r = rand_32(); + 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; +} \ No newline at end of file diff --git a/libs/libhydrogen/impl/random/chibios.h b/libs/libhydrogen/impl/random/chibios.h new file mode 100644 index 0000000..9a1bea4 --- /dev/null +++ b/libs/libhydrogen/impl/random/chibios.h @@ -0,0 +1,23 @@ +#include +#include +#include + +/* Declarations from ChibiOS HAL TRNG module */ + +extern struct hal_trng_driver TRNGD1; + +void trngStart(struct hal_trng_driver *, const void *); +bool trngGenerate(struct hal_trng_driver *, size_t size, uint8_t *); + +static int +hydro_random_init(void) +{ + trngStart(&TRNGD1, NULL); + + if (trngGenerate(&TRNGD1, sizeof hydro_random_context.state, hydro_random_context.state)) { + return -1; + } + hydro_random_context.counter = ~LOAD64_LE(hydro_random_context.state); + + return 0; +} diff --git a/libs/libhydrogen/impl/random/esp32.h b/libs/libhydrogen/impl/random/esp32.h new file mode 100644 index 0000000..145ec61 --- /dev/null +++ b/libs/libhydrogen/impl/random/esp32.h @@ -0,0 +1,32 @@ +// Important: RF *must* be activated on ESP board +// https://techtutorialsx.com/2017/12/22/esp32-arduino-random-number-generation/ +#ifdef ESP32 +# include +#endif + +#ifdef ARDUINO +# include +#endif + +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; + + 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; +} diff --git a/libs/libhydrogen/impl/random/linux_kernel.h b/libs/libhydrogen/impl/random/linux_kernel.h new file mode 100644 index 0000000..1b55bfe --- /dev/null +++ b/libs/libhydrogen/impl/random/linux_kernel.h @@ -0,0 +1,8 @@ +static int +hydro_random_init(void) +{ + get_random_bytes(hydro_random_context.state, sizeof hydro_random_context.state); + hydro_random_context.counter = ~LOAD64_LE(hydro_random_context.state); + + return 0; +} diff --git a/libs/libhydrogen/impl/random/mbed.h b/libs/libhydrogen/impl/random/mbed.h new file mode 100644 index 0000000..8b63fa1 --- /dev/null +++ b/libs/libhydrogen/impl/random/mbed.h @@ -0,0 +1,44 @@ +#include +#include + +#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 diff --git a/libs/libhydrogen/impl/random/nrf52832.h b/libs/libhydrogen/impl/random/nrf52832.h new file mode 100644 index 0000000..5db28d8 --- /dev/null +++ b/libs/libhydrogen/impl/random/nrf52832.h @@ -0,0 +1,41 @@ +// Important: The SoftDevice *must* be activated to enable reading from the RNG +// http://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Frng.html + +#include + +static int +hydro_random_init(void) +{ + const char ctx[hydro_hash_CONTEXTBYTES] = { 'h', 'y', 'd', 'r', 'o', 'P', 'R', 'G' }; + hydro_hash_state st; + const uint8_t total_bytes = 32; + uint8_t remaining_bytes = total_bytes; + uint8_t available_bytes; + uint8_t rand_buffer[32]; + + hydro_hash_init(&st, ctx, NULL); + + for (;;) { + if (sd_rand_application_bytes_available_get(&available_bytes) != NRF_SUCCESS) { + return -1; + } + if (available_bytes > 0) { + if (available_bytes > remaining_bytes) { + available_bytes = remaining_bytes; + } + if (sd_rand_application_vector_get(rand_buffer, available_bytes) != NRF_SUCCESS) { + return -1; + } + hydro_hash_update(&st, rand_buffer, available_bytes); + remaining_bytes -= available_bytes; + } + if (remaining_bytes <= 0) { + break; + } + delay(10); + } + 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; +} diff --git a/libs/libhydrogen/impl/random/particle.h b/libs/libhydrogen/impl/random/particle.h new file mode 100644 index 0000000..bd3a57e --- /dev/null +++ b/libs/libhydrogen/impl/random/particle.h @@ -0,0 +1,26 @@ +// Note: All particle platforms except for the Spark Core have a HW RNG. Only allow building on +// supported platforms for now. PLATFORM_ID definitions: +// https://github.com/particle-iot/device-os/blob/mesh-develop/hal/shared/platforms.h + +#include + +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; + + hydro_hash_init(&st, ctx, NULL); + + while (ebits < 256) { + uint32_t r = HAL_RNG_GetRandomNumber(); + 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; +} diff --git a/libs/libhydrogen/impl/random/pico-sdk.h b/libs/libhydrogen/impl/random/pico-sdk.h new file mode 100644 index 0000000..8c0f889 --- /dev/null +++ b/libs/libhydrogen/impl/random/pico-sdk.h @@ -0,0 +1,35 @@ +#ifndef RANDOM_PICO_H_ +#define RANDOM_PICO_H_ + +#include "pico/rand.h" + +#ifdef __cplusplus +extern "C" { +#endif + +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; + + hydro_hash_init(&st, ctx, NULL); + + while (ebits < 256) { + uint32_t r = get_rand_32(); + 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; +} + +#ifdef __cplusplus +} +#endif + +#endif /* RANDOM_PICO_H_ */ diff --git a/libs/libhydrogen/impl/random/riot.h b/libs/libhydrogen/impl/random/riot.h new file mode 100644 index 0000000..06522ba --- /dev/null +++ b/libs/libhydrogen/impl/random/riot.h @@ -0,0 +1,10 @@ +#include + +static int +hydro_random_init(void) +{ + random_bytes(hydro_random_context.state, sizeof(hydro_random_context.state)); + hydro_random_context.counter = ~LOAD64_LE(hydro_random_context.state); + + return 0; +} diff --git a/libs/libhydrogen/impl/random/rtthread.h b/libs/libhydrogen/impl/random/rtthread.h new file mode 100644 index 0000000..e83b4a3 --- /dev/null +++ b/libs/libhydrogen/impl/random/rtthread.h @@ -0,0 +1,38 @@ +#include +#include + +#define DBG_TAG "libhydrogen" +#define DBG_LVL DBG_LOG +#include + +static int +hydrogen_init(void) +{ + if (hydro_init() != 0) { + abort(); + } + LOG_I("libhydrogen initialized"); + return 0; +} +INIT_APP_EXPORT(hydrogen_init); + +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; + + hydro_hash_init(&st, ctx, NULL); + + while (ebits < 256) { + uint32_t r = rt_hwcrypto_rng_update(); + 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; +} diff --git a/libs/libhydrogen/impl/random/stm32.h b/libs/libhydrogen/impl/random/stm32.h new file mode 100644 index 0000000..8090775 --- /dev/null +++ b/libs/libhydrogen/impl/random/stm32.h @@ -0,0 +1,63 @@ + +// Use hardware RNG peripheral +// Working with HAL, LL Driver (untested) +#if defined(STM32F4) || defined(STM32L4) + +# if defined(STM32F4) +# include "stm32f4xx.h" +# elif defined(STM32L4) +# include "stm32l4xx_hal_rng.h" + +static RNG_HandleTypeDef RngHandle; +# endif + +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; + + __IO uint32_t tmpreg; + +# if defined(STM32F4) + // Enable RNG clock source + SET_BIT(RCC->AHB2ENR, RCC_AHB2ENR_RNGEN); + + // Delay after an RCC peripheral clock enabling + tmpreg = READ_BIT(RCC->AHB2ENR, RCC_AHB2ENR_RNGEN); + UNUSED(tmpreg); + + // RNG Peripheral enable + SET_BIT(RNG->CR, RNG_CR_RNGEN); +# elif defined(STM32L4) + RngHandle.Instance = RNG; + HAL_RNG_Init(&RngHandle); +# endif + + hydro_hash_init(&st, ctx, NULL); + + while (ebits < 256) { + uint32_t r = 0; +# if defined(STM32F4) + while (!(READ_BIT(RNG->SR, RNG_SR_DRDY))) { + } + + r = RNG->DR; +# elif defined(STM32L4) + if (HAL_RNG_GenerateRandomNumber(&RngHandle, &r) != HAL_OK) { + continue; + } +# endif + 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; +} +#else +# error SMT32 implementation missing! +#endif diff --git a/libs/libhydrogen/impl/random/unix.h b/libs/libhydrogen/impl/random/unix.h new file mode 100644 index 0000000..7744d84 --- /dev/null +++ b/libs/libhydrogen/impl/random/unix.h @@ -0,0 +1,85 @@ +#include +#include +#ifdef __linux__ +# include +#endif +#include +#include + +#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; +} diff --git a/libs/libhydrogen/impl/random/wasi.h b/libs/libhydrogen/impl/random/wasi.h new file mode 100644 index 0000000..172e0e2 --- /dev/null +++ b/libs/libhydrogen/impl/random/wasi.h @@ -0,0 +1,12 @@ +#include + +static int +hydro_random_init(void) +{ + if (getentropy(hydro_random_context.state, sizeof hydro_random_context.state) != 0) { + return -1; + } + hydro_random_context.counter = ~LOAD64_LE(hydro_random_context.state); + + return 0; +} diff --git a/libs/libhydrogen/impl/random/windows.h b/libs/libhydrogen/impl/random/windows.h new file mode 100644 index 0000000..32a6721 --- /dev/null +++ b/libs/libhydrogen/impl/random/windows.h @@ -0,0 +1,20 @@ +#include +#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; +} diff --git a/libs/libhydrogen/impl/random/zephyr.h b/libs/libhydrogen/impl/random/zephyr.h new file mode 100644 index 0000000..b6e8ea6 --- /dev/null +++ b/libs/libhydrogen/impl/random/zephyr.h @@ -0,0 +1,13 @@ +#include + +static int +hydro_random_init(void) +{ + if (sys_csrand_get(&hydro_random_context.state, sizeof hydro_random_context.state) != 0) { + return -1; + } + + hydro_random_context.counter = ~LOAD64_LE(hydro_random_context.state); + + return 0; +} diff --git a/libs/libhydrogen/impl/secretbox.h b/libs/libhydrogen/impl/secretbox.h new file mode 100644 index 0000000..9aa1137 --- /dev/null +++ b/libs/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_final(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_final(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_final(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_final(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; +} diff --git a/libs/libhydrogen/impl/sign.h b/libs/libhydrogen/impl/sign.h new file mode 100644 index 0000000..0b21a8a --- /dev/null +++ b/libs/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_PREHASHBYTES); + 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, 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; +} diff --git a/libs/libhydrogen/impl/x25519.h b/libs/libhydrogen/impl/x25519.h new file mode 100644 index 0000000..0fe9586 --- /dev/null +++ b/libs/libhydrogen/impl/x25519.h @@ -0,0 +1,386 @@ +/* + * 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 = -76; + int i; + + for (i = 0; i < hydro_x25519_NLIMBS; i++) { + out[i] = (hydro_x25519_limb_t) (carry = carry + a[i] - b[i]); + carry >>= hydro_x25519_WBITS; + } + hydro_x25519_propagate(out, (hydro_x25519_limb_t) (2 + 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_limb_t b[], + const 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++) { + hydro_x25519_limb_t mand = b[i]; + carry2 = 0; + + 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] = (hydro_x25519_limb_t) (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_SECRETKEYBYTES], + const uint8_t x1[hydro_x25519_PUBLICKEYBYTES], 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] = (hydro_x25519_limb_t) (scarry = scarry + out[i] - hydro_x25519_sc_p[i]); + scarry >>= hydro_x25519_WBITS; + } + hydro_x25519_limb_t need_add = (hydro_x25519_limb_t) - (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]); + } +} diff --git a/libs/libhydrogen/library.properties b/libs/libhydrogen/library.properties new file mode 100644 index 0000000..6b3fabf --- /dev/null +++ b/libs/libhydrogen/library.properties @@ -0,0 +1,10 @@ +architectures=avr,nrf52,particle-photon,particle-electron,particle-p1,esp32 +author=Frank Denis +category=Other +includes=hydrogen.h +maintainer=Frank Denis +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=1.0.0 diff --git a/libs/libhydrogen/logo.png b/libs/libhydrogen/logo.png new file mode 100644 index 0000000..d111ce7 Binary files /dev/null and b/libs/libhydrogen/logo.png differ diff --git a/libs/libhydrogen/meson.build b/libs/libhydrogen/meson.build new file mode 100644 index 0000000..64cd8f1 --- /dev/null +++ b/libs/libhydrogen/meson.build @@ -0,0 +1,67 @@ +project( + 'libhydrogen', + 'c', + license: 'ISC', + default_options: [ + 'buildtype=minsize', + 'default_library=static', + 'warning_level=2', + ], +) + +cc = meson.get_compiler('c') + +cflags = cc.get_supported_arguments( + '-Wbad-function-cast', + '-Wcast-align', + '-Wcast-qual', + '-Wdiv-by-zero', + '-Wfloat-equal', + '-Wmissing-declarations', + '-Wmissing-prototypes', + '-Wnested-externs', + '-Wno-type-limits', + '-Wno-unknown-pragmas', + '-Wpointer-arith', + '-Wredundant-decls', + '-Wstrict-prototypes', + '-Wswitch-enum', + '-fno-exceptions', + '-mtune=native', +) +add_project_arguments(cflags, language: 'c') + +include_dirs = include_directories('.') + +sources = files( + 'hydrogen.c', +) + +libhydrogen = library( + 'hydrogen', + sources, + include_directories: include_dirs, + install: true, +) + +tests = executable( + 'tests', + files('tests/tests.c'), + link_with: libhydrogen, +) +test('tests', tests) + +install_headers(files('hydrogen.h')) + +pkgconfig = import('pkgconfig') +pkgconfig.generate( + libhydrogen, + name: 'libhydrogen', + description: 'Lightweight, secure, easy-to-use crypto library suitable for constrained environments.', + url: 'https://libhydrogen.org/', +) + +libhydrogen_dep = declare_dependency( + include_directories: include_dirs, + link_with: libhydrogen, +) diff --git a/libs/libhydrogen/tests/tests.c b/libs/libhydrogen/tests/tests.c new file mode 100644 index 0000000..15dccdc --- /dev/null +++ b/libs/libhydrogen/tests/tests.c @@ -0,0 +1,493 @@ +#ifdef NDEBUG +# undef NDEBUG +#endif +#include +#include +#include + +#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]; +#ifdef __TRUSTINSOFT_ANALYZER__ + uint8_t msg[32]; +#else + uint8_t msg[1000]; +#endif + 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); +#ifndef __TRUSTINSOFT_ANALYZER__ + assert_streq( + "e5d2beb77a039965850ee76327e06b2fa6cb5121db8038b11bce4641a9c4bd843658104bdf07342570bb5fd1d7" + "2c0d31a8981b47c718fddaffbd4171605c873cbaf921bb57988dd814f3a3fbef9799ff7c762705c4bf37ab2981" + "5981bf0d8833d60afe14", + hex); +#endif + hydro_hash_hash(h, sizeof h, msg, sizeof msg, ctx, key); + hydro_bin2hex(hex, sizeof hex, h, sizeof h); +#ifndef __TRUSTINSOFT_ANALYZER__ + assert_streq( + "724bd8883df73320ffd70923cb997f9a99bc670c4d78887be4975add0099fbf489b266a85d1f56743062d60a05" + "590cbce47e45108367879bf4641cbaefe584e8618cbeb8c230ae956da22c7c5c4f11a8804ca576ec20fa5da239" + "dde3d03a6018383c21f5", + hex); +#endif + hydro_hash_hash(h, hydro_hash_BYTES, msg, sizeof msg, ctx, key); + hydro_bin2hex(hex, sizeof hex, h, hydro_hash_BYTES); +#ifndef __TRUSTINSOFT_ANALYZER__ + assert_streq("7dfa45ce18210e2422fd658bf7beccb6e534e44f99ae359f4af3ba41af8ca463", hex); +#endif + /* total input length is a multiple of the rate */ + hydro_hash_hash(h, hydro_hash_BYTES, msg, 13, ctx, key); + hydro_bin2hex(hex, sizeof hex, h, hydro_hash_BYTES); +#ifndef __TRUSTINSOFT_ANALYZER__ + assert_streq("d57a9800549bb4bab6a06fa6e16e08aad68d7d4313fb69a81b9f5d5af375dbe7", hex); +#endif +} + +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) +{ +#ifdef __TRUSTINSOFT_ANALYZER__ + uint8_t msg[32]; +#else + uint8_t msg[500]; +#endif + 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_kx_nk(void) +{ + hydro_kx_state st_client; + hydro_kx_keypair server_static_kp; + uint8_t psk[hydro_kx_PSKBYTES]; + uint8_t packet1[hydro_kx_NK_PACKET1BYTES]; + uint8_t packet2[hydro_kx_NK_PACKET2BYTES]; + hydro_kx_session_keypair kp_client; + hydro_kx_session_keypair kp_server; + + hydro_kx_keygen(&server_static_kp); + + hydro_kx_nk_1(&st_client, packet1, NULL, server_static_kp.pk); + hydro_kx_nk_2(&kp_server, packet2, packet1, NULL, &server_static_kp); + hydro_kx_nk_3(&st_client, &kp_client, packet2); + + 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_nk_1(&st_client, packet1, psk, server_static_kp.pk); + hydro_kx_nk_2(&kp_server, packet2, packet1, psk, &server_static_kp); + hydro_kx_nk_3(&st_client, &kp_client, packet2); + + 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_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) +{ +#ifdef _MSC_VER + /* + * On Windows, disable the "Abort - Retry - Ignore" GUI dialog that otherwise pops up on + * assertion failure. + */ + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); +#endif + + int ret; + + ret = hydro_init(); + assert(ret == 0); + + test_core(); + test_hash(); + test_kdf(); + test_kx_n(); + test_kx_kk(); + test_kx_xx(); + test_kx_nk(); + test_pwhash(); + test_randombytes(); + test_secretbox(); + test_sign(); + + return 0; +} diff --git a/libs/libhydrogen/tis-ci/common.config b/libs/libhydrogen/tis-ci/common.config new file mode 100644 index 0000000..a57b261 --- /dev/null +++ b/libs/libhydrogen/tis-ci/common.config @@ -0,0 +1,8 @@ +{ + "files": [ "../tests/tests.c", "../hydrogen.c" ], + "filesystem": { + "files": [ { "name": "/dev/urandom", "from": "urandom_1" } ] + }, + "val-timeout": 10800, + "no-results": "true" +} diff --git a/libs/libhydrogen/tis-ci/gcc_ppc_32.config b/libs/libhydrogen/tis-ci/gcc_ppc_32.config new file mode 100644 index 0000000..c9af973 --- /dev/null +++ b/libs/libhydrogen/tis-ci/gcc_ppc_32.config @@ -0,0 +1,4 @@ +{ + "machdep": "gcc_ppc_32", + "compilation_cmd": "-I../. -U__SSE2__ -Dvolatile= -D__BYTE_ORDER__=__ORDER_BIG_ENDIAN__ -U__SIZEOF_INT128__" +} diff --git a/libs/libhydrogen/tis-ci/gcc_ppc_64.config b/libs/libhydrogen/tis-ci/gcc_ppc_64.config new file mode 100644 index 0000000..43b4099 --- /dev/null +++ b/libs/libhydrogen/tis-ci/gcc_ppc_64.config @@ -0,0 +1,4 @@ +{ + "machdep": "gcc_ppc_64", + "compilation_cmd": "-I../. -U__SSE2__ -Dvolatile= -D__BYTE_ORDER__=__ORDER_BIG_ENDIAN__" +} diff --git a/libs/libhydrogen/tis-ci/gcc_x86_32.config b/libs/libhydrogen/tis-ci/gcc_x86_32.config new file mode 100644 index 0000000..de09dad --- /dev/null +++ b/libs/libhydrogen/tis-ci/gcc_x86_32.config @@ -0,0 +1,4 @@ +{ + "machdep": "gcc_x86_32", + "compilation_cmd": "-I../. -U__SSE2__ -Dvolatile=" +} diff --git a/libs/libhydrogen/tis-ci/gcc_x86_64.config b/libs/libhydrogen/tis-ci/gcc_x86_64.config new file mode 100644 index 0000000..3a478ee --- /dev/null +++ b/libs/libhydrogen/tis-ci/gcc_x86_64.config @@ -0,0 +1,4 @@ +{ + "machdep": "gcc_x86_64", + "compilation_cmd": "-I../. -U__SSE2__ -Dvolatile=" +} diff --git a/libs/libhydrogen/tis-ci/urandom_1 b/libs/libhydrogen/tis-ci/urandom_1 new file mode 100644 index 0000000..306fe3c Binary files /dev/null and b/libs/libhydrogen/tis-ci/urandom_1 differ diff --git a/libs/libhydrogen/tis.config b/libs/libhydrogen/tis.config new file mode 100644 index 0000000..a578b6e --- /dev/null +++ b/libs/libhydrogen/tis.config @@ -0,0 +1,123 @@ +[ + { + "name": "hash - gcc_x86_32", + "main": "test_hash", + "include": "tis-ci/gcc_x86_32.config", + "include": "tis-ci/common.config" + }, + { + "name": "hash - gcc_x86_64", + "main": "test_hash", + "include": "tis-ci/gcc_x86_64.config", + "include": "tis-ci/common.config" + }, + { + "name": "hash - gcc_ppc_32", + "main": "test_hash", + "include": "tis-ci/gcc_ppc_32.config", + "include": "tis-ci/common.config" + + }, + { + "name": "hash - gcc_ppc_64", + "main": "test_hash", + "include": "tis-ci/gcc_ppc_64.config", + "include": "tis-ci/common.config" + }, + { + "name": "kdf - gcc_x86_32", + "main": "test_kdf", + "include": "tis-ci/gcc_x86_32.config", + "include": "tis-ci/common.config" + }, + { + "name": "kdf - gcc_x86_64", + "main": "test_kdf", + "include": "tis-ci/gcc_x86_64.config", + "include": "tis-ci/common.config" + }, + { + "name": "kdf - gcc_ppc_32", + "main": "test_kdf", + "include": "tis-ci/gcc_ppc_32.config", + "include": "tis-ci/common.config" + }, + { + "name": "kdf - gcc_ppc_64", + "main": "test_kdf", + "include": "tis-ci/gcc_ppc_64.config", + "include": "tis-ci/common.config" + }, + { + "name": "pwhash - gcc_x86_32", + "main": "test_pwhash", + "include": "tis-ci/gcc_x86_32.config", + "include": "tis-ci/common.config" + }, + { + "name": "pwhash - gcc_x86_64", + "main": "test_pwhash", + "include": "tis-ci/gcc_x86_64.config", + "include": "tis-ci/common.config" + }, + { + "name": "pwhash - gcc_ppc_32", + "main": "test_pwhash", + "include": "tis-ci/gcc_ppc_32.config", + "include": "tis-ci/common.config" + }, + { + "name": "pwhash - gcc_ppc_64", + "main": "test_pwhash", + "include": "tis-ci/gcc_ppc_64.config", + "include": "tis-ci/common.config" + }, + { + "name": "secretbox - gcc_x86_32", + "main": "test_secretbox", + "include": "tis-ci/gcc_x86_32.config", + "include": "tis-ci/common.config" + }, + { + "name": "secretbox - gcc_x86_64", + "main": "test_secretbox", + "include": "tis-ci/gcc_x86_64.config", + "include": "tis-ci/common.config" + }, + { + "name": "secretbox - gcc_ppc_32", + "main": "test_secretbox", + "include": "tis-ci/gcc_ppc_32.config", + "include": "tis-ci/common.config" + }, + { + "name": "secretbox - gcc_ppc_64", + "main": "test_secretbox", + "include": "tis-ci/gcc_ppc_64.config", + "include": "tis-ci/common.config" + }, + { + "name": "sign - gcc_x86_32", + "main": "test_sign", + "include": "tis-ci/gcc_x86_32.config", + "include": "tis-ci/common.config" + }, + { + "name": "sign - gcc_x86_64", + "main": "test_sign", + "include": "tis-ci/gcc_x86_64.config", + "include": "tis-ci/common.config" + }, + { + "name": "sign - gcc_ppc_32", + "main": "test_sign", + "include": "tis-ci/gcc_ppc_32.config", + "include": "tis-ci/common.config" + }, + { + "name": "sign - gcc_ppc_64", + "main": "test_sign", + "include": "tis-ci/gcc_ppc_64.config", + "include": "tis-ci/common.config" + } +] diff --git a/src/work-calendar.cpp b/src/work-calendar.cpp index 9b2c8c7..c1dc4b6 100644 --- a/src/work-calendar.cpp +++ b/src/work-calendar.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "inter.h" #include "inter-bold.h" @@ -52,7 +53,7 @@ int weekday_from_day(int year, int month, int day) { int calendar_week_from_day(int year, int month, int day){ int days_sum[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; int days = days_sum[month - 1]; - if(is_leap_year(year) && month > 2) + if (is_leap_year(year) && month > 2) days++; return (10 + days + day - weekday_from_day(year, month, day)) / 7; @@ -66,7 +67,18 @@ int days_per_month(int year, int month){ return days_in_month[month - 1]; } -void init(){ +#define HYDROGEN_CONTEXT "WorkCalS" +#define HYDROGEN_OPSLIMIT 10000 +#define HYDROGEN_MEMLIMIT 0 +#define HYDROGEN_THREADS 1 +static uint8_t master_key[hydro_pwhash_MASTERKEYBYTES] = { 0x6c, 0x2e, 0xed, 0x47, 0x36, 0x29, 0xda, 0x11, 0x6e, 0xf4, 0x41, 0x66, 0x3b, 0xd7, 0xfa, 0x72, 0xf7, 0x51, 0x48, 0x6d, 0x10, 0x7b, 0xa5, 0x04, 0x00, 0x11, 0x2e, 0xc4, 0xf2, 0xdb, 0x77, 0x51 }; +static uint8_t derived_key[hydro_secretbox_KEYBYTES]; + +static char password_input_buffer[256]; +static char password_confirmation_input_buffer[256]; + +void init() { + hydro_init(); ImGuiIO &io = ImGui::GetIO(); inter_regular = io.Fonts->AddFontFromMemoryCompressedTTF(inter_compressed_data, inter_compressed_size); inter_bold = io.Fonts->AddFontFromMemoryCompressedTTF(inter_bold_compressed_data, inter_bold_compressed_size); @@ -103,68 +115,115 @@ static uint32_t num_categorized_days = 0; static Categorized_Day categorized_days[365*50]; //Colors -ImVec4 table_white_bg = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); -ImVec4 table_saturday_bg = ImVec4(0.0f, 0.0f, 0.0f, 0.1f); -ImVec4 table_sunday_bg = ImVec4(0.0f, 0.0f, 0.0f, 0.2f); +static ImVec4 table_white_bg = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); +static ImVec4 table_saturday_bg = ImVec4(0.0f, 0.0f, 0.0f, 0.1f); +static ImVec4 table_sunday_bg = ImVec4(0.0f, 0.0f, 0.0f, 0.2f); -ImVec4 table_hover_color = ImVec4(0.0f, 0.3f, 0.6f, 0.2f); +static ImVec4 table_hover_color = ImVec4(0.0f, 0.3f, 0.6f, 0.2f); + +static char plaintext_buffer[2 * 1024 * 1024] = {}; +static char encrypted_buffer[hydro_secretbox_HEADERBYTES + sizeof(plaintext_buffer)]; void save(){ - if(!save_file_path) return; + if (!save_file_path) return; uint32_t version = 1; - FILE *save_file = fopen(save_file_path, "wb"); - fwrite(&version, sizeof(version), 1, save_file); - fwrite(&num_categories, sizeof(num_categories), 1, save_file); - fwrite(&num_categorized_days, sizeof(num_categorized_days), 1, save_file); + #define write_to_buffer(x) memcpy(plaintext_buffer + offset, &(x), sizeof(x)); offset += sizeof(x); + + size_t offset = 0; + write_to_buffer(version); + write_to_buffer(num_categories); + write_to_buffer(num_categorized_days); for (int i = 0; i < num_categories; i++) { - fwrite(&categories[i].color, sizeof(categories[i].color), 1, save_file); - fwrite(&categories[i].name, sizeof(categories[i].name), 1, save_file); + write_to_buffer(categories[i].color); + write_to_buffer(categories[i].name); } for (int i = 0; i < num_categorized_days; i++) { - fwrite(&categorized_days[i].year, sizeof(categorized_days[i].year), 1, save_file); - fwrite(&categorized_days[i].month, sizeof(categorized_days[i].month), 1, save_file); - fwrite(&categorized_days[i].day, sizeof(categorized_days[i].day), 1, save_file); - fwrite(&categorized_days[i].category, sizeof(categorized_days[i].category), 1, save_file); + write_to_buffer(categorized_days[i].year); + write_to_buffer(categorized_days[i].month); + write_to_buffer(categorized_days[i].day); + write_to_buffer(categorized_days[i].category); } + + #undef write_to_buffer + if (hydro_secretbox_encrypt((uint8_t *)encrypted_buffer, plaintext_buffer, offset, 0, HYDROGEN_CONTEXT, derived_key) != 0) { + return; //TODO error message + } + hydro_memzero(plaintext_buffer, sizeof(plaintext_buffer)); + + FILE *save_file = fopen(save_file_path, "wb"); + fwrite(encrypted_buffer, hydro_secretbox_HEADERBYTES + offset, 1, save_file); fclose (save_file); + hydro_memzero(encrypted_buffer, sizeof(encrypted_buffer)); + + char *title = (char *)calloc(1, strlen("Work Calendar - ") + strlen(save_file_path) + 1); + memcpy(title, "Work Calendar - ", strlen("Work Calendar - ")); + memcpy(title + strlen("Work Calendar - "), save_file_path, strlen(save_file_path)); + + set_window_title(title); + free(title); } void load(){ + FILE *save_file = fopen(save_file_path, "rb"); - uint32_t version = 0; - fread(&version, sizeof(version), 1, save_file); - if(version != 1) { + size_t num_bytes = fread(encrypted_buffer, 1, sizeof(encrypted_buffer), save_file); + fclose (save_file); + + if ( num_bytes <= 0) { return; //TODO Popup warning } - fread(&num_categories, sizeof(num_categories), 1, save_file); - fread(&num_categorized_days, sizeof(num_categorized_days), 1, save_file); + if (hydro_secretbox_decrypt(plaintext_buffer, (uint8_t *)encrypted_buffer, num_bytes, 0, HYDROGEN_CONTEXT, derived_key) != 0){ + return; //TODO Popup warning + } + hydro_memzero(encrypted_buffer, sizeof(encrypted_buffer)); - if(num_categories > IM_ARRAYSIZE(categories) || num_categorized_days > IM_ARRAYSIZE(categorized_days)){ + #define read_from_buffer(x) memcpy(&(x), plaintext_buffer + offset, sizeof(x)); offset += sizeof(x); + + size_t offset = 0; + uint32_t version = 0; + read_from_buffer(version); + if (version != 1) { + return; //TODO Popup warning + } + + read_from_buffer(num_categories); + read_from_buffer(num_categorized_days); + + if (num_categories > IM_ARRAYSIZE(categories) || num_categorized_days > IM_ARRAYSIZE(categorized_days)){ return; //TODO Popup warning } for (int i = 0; i < num_categories; i++) { - fread(&categories[i].color, sizeof(categories[i].color), 1, save_file); - fread(&categories[i].name, sizeof(categories[i].name), 1, save_file); + read_from_buffer(categories[i].color); + read_from_buffer(categories[i].name); } for (int i = 0; i < num_categorized_days; i++) { - fread(&categorized_days[i].year, sizeof(categorized_days[i].year), 1, save_file); - fread(&categorized_days[i].month, sizeof(categorized_days[i].month), 1, save_file); - fread(&categorized_days[i].day, sizeof(categorized_days[i].day), 1, save_file); - fread(&categorized_days[i].category, sizeof(categorized_days[i].category), 1, save_file); + read_from_buffer(categorized_days[i].year); + read_from_buffer(categorized_days[i].month); + read_from_buffer(categorized_days[i].day); + read_from_buffer(categorized_days[i].category); } + #undef read_from_buffer - fclose (save_file); + hydro_memzero(plaintext_buffer, sizeof(plaintext_buffer)); + + char *title = (char *)calloc(1, strlen("Work Calendar - ") + strlen(save_file_path) + 1); + memcpy(title, "Work Calendar - ", strlen("Work Calendar - ")); + memcpy(title + strlen("Work Calendar - "), save_file_path, strlen(save_file_path)); + + set_window_title(title); + free(title); } void per_frame(){ ImGuiStyle &style = ImGui::GetStyle(); + ImVec2 center = ImGui::GetMainViewport()->GetCenter(); year_min_size = ImGui::CalcTextSize("8888").x + 5; @@ -183,26 +242,17 @@ void per_frame(){ ImGui::DockSpaceOverViewport(main_viewport_dock, ImGui::GetMainViewport(), ImGuiDockNodeFlags_NoTabBar); ImGuiID close_popup = ImGui::GetID("Close"); - ImGuiID open_popup = ImGui::GetID("Open"); - ImGuiID save_popup = ImGui::GetID("Save"); - ImGuiID logout_popup = ImGui::GetID("Logout"); //TODO - ImGuiID about_popup = ImGui::GetID("About"); + ImGuiID save_password_popup = ImGui::GetID("Passwort eingeben##Save"); + ImGuiID open_password_popup = ImGui::GetID("Passwort eingeben##Open"); if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("Datei")) { if (ImGui::MenuItem("Öffnen", "Ctrl + O")) { char *new_save_file_path = open_file_dialog(); - if(new_save_file_path) { - if(save_file_path) free(save_file_path); + if (new_save_file_path) { + if (save_file_path) free(save_file_path); save_file_path = new_save_file_path; - load(); - - char *title = (char *)calloc(1, strlen("Work Calendar - ") + strlen(save_file_path) + 1); - memcpy(title, "Work Calendar - ", strlen("Work Calendar - ")); - memcpy(title + strlen("Work Calendar - "), save_file_path, strlen(save_file_path)); - - set_window_title(title); - free(title); + ImGui::OpenPopup(open_password_popup); } } if (ImGui::MenuItem("Speichern", "Ctrl + S", false, !!save_file_path)) { @@ -211,23 +261,27 @@ void per_frame(){ if (ImGui::MenuItem("Speichern unter", "Ctrl + Shift + S")) { char *new_save_file_path = save_file_dialog(); - if(new_save_file_path) { - if(save_file_path) free(save_file_path); + if (new_save_file_path) { + if (save_file_path) free(save_file_path); save_file_path = new_save_file_path; - save(); - - char *title = (char *)calloc(1, strlen("Work Calendar - ") + strlen(save_file_path) + 1); - memcpy(title, "Work Calendar - ", strlen("Work Calendar - ")); - memcpy(title + strlen("Work Calendar - "), save_file_path, strlen(save_file_path)); - - set_window_title(title); - free(title); + ImGui::OpenPopup(save_password_popup); } } //TODO ImGui::Separator(); - ImGui::MenuItem("Kalender Schließen", "Ctrl + X"); + if (ImGui::MenuItem("Kalender Schließen", "Ctrl + X")) { + //TODO if calendar is opened warning + + if (save_file_path) free(save_file_path); + save_file_path = NULL; + num_categories = 0; + num_categorized_days = 0; + hydro_memzero(categories, sizeof(categories)); + hydro_memzero(categorized_days, sizeof(categorized_days)); + hydro_memzero(derived_key, sizeof(derived_key)); + set_window_title("Work Calendar"); + } ImGui::Separator(); if (ImGui::MenuItem("Beenden", NULL)) { @@ -254,7 +308,78 @@ void per_frame(){ ImGui::EndMainMenuBar(); } - ImVec2 center = ImGui::GetMainViewport()->GetCenter(); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + if (ImGui::BeginPopupModal("Passwort eingeben##Save", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + + ImGui::AlignTextToFramePadding(); + ImGui::Text("Passwort"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(-1); + ImGui::InputText("##Passwort", password_input_buffer, sizeof(password_input_buffer), ImGuiInputTextFlags_Password); + + ImGui::AlignTextToFramePadding(); + ImGui::Text("Passwort bestätigen"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(-1); + ImGui::InputText("##Passwort bestätigen", password_confirmation_input_buffer, sizeof(password_confirmation_input_buffer), ImGuiInputTextFlags_Password); + + ImGui::PopStyleVar(); + + if (ImGui::Button("OK", ImVec2(120, 0))) { + if (hydro_compare((uint8_t *)password_input_buffer, (uint8_t *) password_confirmation_input_buffer, sizeof(password_input_buffer)) == 0) { + hydro_pwhash_deterministic(derived_key, sizeof derived_key, password_input_buffer, strlen(password_input_buffer), HYDROGEN_CONTEXT, + master_key, HYDROGEN_OPSLIMIT, HYDROGEN_MEMLIMIT, HYDROGEN_THREADS); + save(); + ImGui::CloseCurrentPopup(); + hydro_memzero(password_input_buffer, sizeof(password_input_buffer)); + hydro_memzero(password_confirmation_input_buffer, sizeof(password_confirmation_input_buffer)); + } + //TODO color input red + message + } + + ImGui::SameLine(); + if (ImGui::Button("Abbrechen", ImVec2(120, 0))) { + ImGui::CloseCurrentPopup(); + hydro_memzero(password_input_buffer, sizeof(password_input_buffer)); + hydro_memzero(password_confirmation_input_buffer, sizeof(password_confirmation_input_buffer)); + } + + ImGui::EndPopup(); + } + + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + if (ImGui::BeginPopupModal("Passwort eingeben##Open", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + + ImGui::AlignTextToFramePadding(); + ImGui::Text("Passwort"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(-1); + ImGui::InputText("##Passwort", password_input_buffer, sizeof(password_input_buffer), ImGuiInputTextFlags_Password); + + ImGui::PopStyleVar(); + + if (ImGui::Button("OK", ImVec2(120, 0))) { + hydro_pwhash_deterministic(derived_key, sizeof derived_key, password_input_buffer, strlen(password_input_buffer), HYDROGEN_CONTEXT, + master_key, HYDROGEN_OPSLIMIT, HYDROGEN_MEMLIMIT, HYDROGEN_THREADS); + hydro_memzero(password_input_buffer, sizeof(password_input_buffer)); + + load(); + ImGui::CloseCurrentPopup(); + } + + ImGui::SameLine(); + if (ImGui::Button("Abbrechen", ImVec2(120, 0))) { + if (save_file_path) free(save_file_path); + save_file_path = NULL; + ImGui::CloseCurrentPopup(); + hydro_memzero(password_input_buffer, sizeof(password_input_buffer)); + } + + ImGui::EndPopup(); + } + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); if (ImGui::BeginPopupModal("Close", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("Möchten Sie wirklich beenden?"); @@ -278,7 +403,7 @@ void per_frame(){ ImGui::PushStyleColor(ImGuiCol_HeaderHovered, {0,0,0,0}); ImGui::PushStyleColor(ImGuiCol_HeaderActive, {0,0,0,0}); - if(ImGui::BeginTable("CalendarHeader", 5)) { + if (ImGui::BeginTable("CalendarHeader", 5)) { ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); // Left side ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed); // Prev year @@ -309,7 +434,7 @@ void per_frame(){ ImGui::PushStyleColor(ImGuiCol_TableBorderStrong, {0,0,0,0}); ImGui::PushStyleColor(ImGuiCol_TableBorderLight, {0,0,0,0}); - if(ImGui::BeginTable("Month", 1, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX)) { + if (ImGui::BeginTable("Month", 1, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX)) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::TextAligned(0.5, -FLT_MIN, month_names[month - 1]); @@ -318,7 +443,7 @@ void per_frame(){ ImGui::TableSetColumnIndex(0); //calendar week table - if(ImGui::BeginTable("CalendarWeek", 1, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_Borders)){ + if (ImGui::BeginTable("CalendarWeek", 1, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_Borders)){ ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::TextAligned(0.5, -FLT_MIN, "KW"); @@ -326,13 +451,13 @@ void per_frame(){ for (int day = 1; day <= days_per_month(year, month); day++) { int weekday = weekday_from_day(year, month, day); - if(weekday == 0 || day == 1){ + if (weekday == 0 || day == 1){ ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); //if first week has 3 or less days, last week from previous year int calender_week = calendar_week_from_day(year, month, day); - if(calender_week == 0) { + if (calender_week == 0) { ImGui::TextAligned(0.5, -FLT_MIN, "%d", 53); } else { ImGui::TextAligned(0.5, -FLT_MIN, "%d", calender_week); @@ -347,7 +472,7 @@ void per_frame(){ ImGui::BeginGroup(); ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0); - if(ImGui::BeginTable("Weekdays", 7, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_Borders)){ + if (ImGui::BeginTable("Weekdays", 7, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_Borders)){ ImGui::TableSetupColumn("Mo"); ImGui::TableSetupColumn("Di"); @@ -366,13 +491,13 @@ void per_frame(){ ImGui::PushStyleColor(ImGuiCol_TableBorderLight, {119/255.0f,119/255.0f,119/255.0f,83/255.0f}); //days of month table - if(ImGui::BeginTable("Weekdays", 7, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_Borders)) { + if (ImGui::BeginTable("Weekdays", 7, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_Borders)) { int offset = weekday_from_day(year, month, 1); for (int day = 1; day <= days_per_month(2025, month); day++) { int weekday = weekday_from_day(year, month, day); - if(weekday == 0 || day == 1){ + if (weekday == 0 || day == 1){ ImGui::TableNextRow(); } @@ -381,12 +506,12 @@ void per_frame(){ ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color_workweek); //weekend coloring - if(weekday == 5){ + if (weekday == 5){ ImU32 cell_bg_color_sat = ImGui::GetColorU32(table_saturday_bg); ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color_sat); } - if(weekday == 6){ + if (weekday == 6){ ImU32 cell_bg_color_sun = ImGui::GetColorU32(table_sunday_bg); ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color_sun); } @@ -401,15 +526,15 @@ void per_frame(){ color.Value.w = 0.5; //sat + sun: add alpha value - if(weekday == 5) + if (weekday == 5) color.Value.w = color.Value.w + table_saturday_bg.w; - if(weekday == 6) + if (weekday == 6) color.Value.w = color.Value.w + table_sunday_bg.w; ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImU32(color)); } - if(current_year == year && current_month == month && current_day == day ) { + if (current_year == year && current_month == month && current_day == day ) { ImGui::PushFont(inter_bold); ImGui::TextAligned(0.5, -FLT_MIN, "%d", day); ImGui::PopFont(); @@ -417,8 +542,8 @@ void per_frame(){ ImGui::TextAligned(0.5, -FLT_MIN, "%d", day); } - if(ImGui::IsItemHovered()){ - if(selected_category != -1) { + if (ImGui::IsItemHovered()){ + if (selected_category != -1) { ImColor hover_color = categories[selected_category].color; hover_color.Value.w = 0.2; ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGui::GetColorU32(ImU32(hover_color))); @@ -485,7 +610,7 @@ void per_frame(){ // Legende if (legend_visible && ImGui::Begin("Legende", 0)) { - if(ImGui::BeginTable("Legend", 4, ImGuiTableFlags_SizingStretchProp)) { + if (ImGui::BeginTable("Legend", 4, ImGuiTableFlags_SizingStretchProp)) { ImGui::TableSetupColumn("Color", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("Edit", ImGuiTableColumnFlags_WidthFixed); @@ -506,7 +631,7 @@ void per_frame(){ ImGui::TableSetColumnIndex(1); if (category.editing) { ImGui::SetNextItemWidth(-1); - if(ImGui::InputText("##name", category.name, IM_ARRAYSIZE(category.name), ImGuiInputTextFlags_EnterReturnsTrue)) { + if (ImGui::InputText("##name", category.name, IM_ARRAYSIZE(category.name), ImGuiInputTextFlags_EnterReturnsTrue)) { category.editing = false; } } else {