Compare commits

16 Commits

Author SHA1 Message Date
Ammerhai b796588133 add shortcuts 2026-05-26 16:42:52 +02:00
Ammerhai 13871d78c4 add licenses
#10
2026-05-26 16:16:17 +02:00
Ammerhai 943b45bcb7 add popups
- close calendar
- close calendar and load a new one
- close application

#16
2026-05-23 16:12:12 +02:00
Ammerhai 8b1ed44bc9 fix save_file_path allocation when open calendar is canceled
remove LS_CLOSE

#9
2026-05-23 15:23:57 +02:00
Ammerhai 37f54f3dee add icons
add visual feedback when changes were made

#9
2026-05-23 15:07:38 +02:00
Ammerhai 5883f457a3 add localization
keep selected language after closing

#17
2026-05-10 13:30:08 +02:00
Ammerhai b4e889cf33 fix to get the leap years correctly displayed 2026-04-09 20:44:27 +02:00
Ammerhai e46ae17b75 remove .cal files
no initial days needed
2026-04-05 21:54:30 +02:00
Ammerhai b0a6e61917 add calendar to ignore 2026-04-03 20:35:51 +02:00
Ammerhai c277ab10d9 add encrypting for save files
add libhydrogen for encrypting and decrypting

#9
2026-04-03 14:59:37 +02:00
Sven Balzer abb6556fd4 implement open_file_dialog, save_file_dialog and set_window_title for linux 2026-03-23 12:42:48 +01:00
Ammerhai 234e056965 add Open, Save calendar functionality
add path information to window title

#9
2026-03-21 13:08:09 +01:00
Ammerhai 01a1187b3c add readme 2026-03-15 16:37:33 +01:00
Sven Balzer 73777e83ab respect should_exit on linux as well 2025-12-06 18:59:07 +01:00
Sven Balzer 793b69d9d9 Fix using a c++23 extension with labels at the end of a block and fix writable strings warnings. 2025-12-06 17:02:54 +01:00
Sven Balzer 2e95908242 Fix deletion of categories to actually delete the one clicked and not the last one.
Also remove categorized days that had the deleted category.
2025-12-05 22:27:45 +01:00
75 changed files with 11269 additions and 102 deletions
+1
View File
@@ -1,2 +1,3 @@
/build/
imgui.ini
*.wcl
+10
View File
@@ -28,7 +28,11 @@ target_include_directories(imgui PUBLIC
libs/imgui/backends
)
# LibHydrogen
add_subdirectory(libs/libhydrogen EXCLUDE_FROM_ALL)
add_executable(work-calendar
src/localization.c
$<$<PLATFORM_ID:Windows>:src/main_win32.cpp>
$<$<PLATFORM_ID:Linux>:src/main_linux.cpp>
)
@@ -36,9 +40,15 @@ add_executable(work-calendar
target_link_libraries(work-calendar
PRIVATE
imgui
hydrogen::hydrogen
$<$<PLATFORM_ID:Windows>:D3D11>
$<$<PLATFORM_ID:Linux>:
vulkan
glfw
>
)
target_compile_definitions(work-calendar
PRIVATE
$<$<PLATFORM_ID:Windows>:_CRT_SECURE_NO_WARNINGS=1>
)
+1
View File
@@ -0,0 +1 @@
# WORK CALENDAR
+165
View File
@@ -0,0 +1,165 @@
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: true
AlignConsecutiveAssignments: true
AlignConsecutiveBitFields: true
AlignConsecutiveDeclarations: true
AlignEscapedNewlines: true
AlignOperands: true
AlignTrailingComments: false
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: TopLevelDefinitions
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: true
BreakBeforeBraces: WebKit
BreakBeforeInheritanceComma: true
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 100
CommentPragmas: "^ IWYU pragma:"
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
DeriveLineEnding: true
DerivePointerAlignment: true
DisableFormat: false
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: true
FixNamespaceComments: false
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
StatementAttributeLikeMacros:
- Q_EMIT
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: ".*"
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: "(Test)?$"
IncludeIsMainSourceRegex: ""
IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: AfterHash
IndentExternBlock: AfterExternBlock
IndentRequires: false
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ""
MacroBlockEnd: ""
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 4
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PenaltyIndentedWhitespace: 0
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SortJavaStaticImport: Before
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
BitFieldColonSpacing: Both
Standard: Latest
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
---
+47
View File
@@ -0,0 +1,47 @@
{
"additional_commands": {
"add_custom_command": {
"kwargs": {
"ARGS": "*",
"COMMAND": 1,
"OUTPUT": 1,
"WORKING_DIRECTORY": 1
}
},
"configure_package_config_file": {
"kwargs": {
"INSTALL_DESTINATION": 1
}
},
"include_directories": {
"kwargs": {
"SYSTEM": 0
}
},
"list": {
"kwargs": {
"APPEND": 1,
"OUTPUT_VARIABLE": 1,
"PREPEND": 1,
"TRANSFORM": 1
}
},
"set_target_properties": {
"kwargs": {
"EXCLUDE_FROM_ALL": 1,
"EXCLUDE_FROM_DEFAULT_BUILD": 1,
"PROPERTIES": 0
}
},
"string": {
"kwargs": {
"APPEND": 1,
"CONCAT": 1
}
}
},
"keyword_case": "upper",
"line_width": 99,
"max_subargs_per_line": 6
}
+56
View File
@@ -0,0 +1,56 @@
name: CI
on:
push:
branches: [master]
pull_request:
branches: [master]
workflow_dispatch:
permissions:
contents: read
jobs:
zig:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Update packages list
run: sudo apt-get update
- name: Install Zig
uses: mlugg/setup-zig@v2
with:
version: master
- name: Build
run: |
zig build
zig build -Dtarget=x86_64-linux
zig build -Dtarget=aarch64-linux
zig build -Dtarget=x86_64-windows
zig build -Dtarget=aarch64-windows
zig build -Dtarget=x86_64-macos
zig build -Dtarget=aarch64-macos
zig build -Dtarget=wasm32-wasi
zig build -Drelease
rm -fr zig-cache zig-out
make:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Update packages list
run: sudo apt-get update
- name: Install Make
run: sudo apt-get install make
- name: Build
run: make
- name: Test
run: make test
+58
View File
@@ -0,0 +1,58 @@
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master" ]
schedule:
- cron: '18 19 * * 2'
jobs:
analyze:
name: Analyze
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'c-cpp' ]
autobuild_force_build_system: ['cmake', 'make', 'meson']
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Maybe remove non-CMake build systems
if: matrix.autobuild_force_build_system == 'cmake'
run: |
rm -vrf Makefile* meson.build
- name: Maybe remove non-Make build systems
if: matrix.autobuild_force_build_system == 'make'
run: |
rm -vrf CMakeLists.txt cmake/ meson.build
- name: Maybe remove non-Meson build systems
if: matrix.autobuild_force_build_system == 'meson'
run: |
rm -vrf Makefile* CMakeLists.txt cmake/
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"
+17
View File
@@ -0,0 +1,17 @@
name: Close inactive issues
on:
schedule:
- cron: "30 1 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v10
with:
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
repo-token: ${{ secrets.GITHUB_TOKEN }}
+33
View File
@@ -0,0 +1,33 @@
*.bc
*.dSYM
*.done
*.final
*.gcda
*.gcno
*.i
*.la
*.lo
*.log
*.mem
*.nexe
*.o
*.plist
*.scan
*.sdf
*.status
*.su
*.tar.*
*~
.DS_Store
.deps
.dirstamp
.done
.idea
.libs
build.options.json
cmake-build-*
coverage.info
depcomp
hydrogen-crypto.zip
libhydrogen.a
tests/tests
+31
View File
@@ -0,0 +1,31 @@
language: c
sudo: false
dist: bionic
env:
- CFLAGS="-Wall -W -O"
os:
- linux
compiler:
- clang
- gcc
- g++
arch:
- arm64
- amd64
- s390x
- ppc64le
addons:
apt:
packages:
- p7zip-full
script:
- make
- make test
- make clean
- make -f Makefile.arduino package
+18
View File
@@ -0,0 +1,18 @@
cff-version: 1.2.0
title: libhydrogen
message: >-
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
+193
View File
@@ -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 ---
$<$<OR:$<C_COMPILER_ID:AppleClang>,$<C_COMPILER_ID:Clang>,$<C_COMPILER_ID:GNU>>:
# ---- Definitions ----
$<$<BOOL:${target_device}>:-DHYDRO_TARGET_DEVICE_${target_device}>
# ---- Optimizations ----
-Os $<$<BOOL:${target_arch}>:-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 ---
$<$<C_COMPILER_ID:MSVC>:
# ---- Definitions ----
$<$<BOOL:${target_device}>:/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 $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
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 $<CONFIGURATION> --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)
+18
View File
@@ -0,0 +1,18 @@
/*
* ISC License
*
* Copyright (c) 2017-2026
* Frank Denis <j at pureftpd dot org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+61
View File
@@ -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
+52
View File
@@ -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)
+43
View File
@@ -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
+40
View File
@@ -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)
+38
View File
@@ -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)].
<a href="https://github.com/jedisct1/libhydrogen/graphs/contributors"><img src="https://opencollective.com/libhydrogen/contributors.svg?width=890&button=false" /></a>
+32
View File
@@ -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,
});
}
+13
View File
@@ -0,0 +1,13 @@
.{
.name = .libhydrogen,
.version = "1.0.0",
.fingerprint = 0x98fa0ad39dcdc390,
.paths = .{
"build.zig",
"build.zig.zon",
"hydrogen.c",
"hydrogen.h",
"impl",
},
}
@@ -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")
@@ -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)
@@ -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")
+19
View File
@@ -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"
+337
View File
@@ -0,0 +1,337 @@
#ifndef hydrogen_H
#define hydrogen_H
#if !(defined(__linux__) && defined(__KERNEL__))
# include <stdbool.h>
# include <stdint.h>
# include <stdlib.h>
#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
+334
View File
@@ -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 <errno.h>
# include <limits.h>
# include <stdbool.h>
# include <stdint.h>
# include <stdlib.h>
# include <string.h>
#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 };
+221
View File
@@ -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);
}
+25
View File
@@ -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
}
@@ -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;
}
}
}
+112
View File
@@ -0,0 +1,112 @@
#include <emmintrin.h>
#ifdef __SSSE3__
# include <tmmintrin.h>
#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);
}
+142
View File
@@ -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);
}
+82
View File
@@ -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]);
+20
View File
@@ -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);
}
+535
View File
@@ -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;
}
+281
View File
@@ -0,0 +1,281 @@
#define hydro_pwhash_ENC_ALGBYTES 1
#define hydro_pwhash_HASH_ALGBYTES 1
#define hydro_pwhash_THREADSBYTES 1
#define hydro_pwhash_OPSLIMITBYTES 8
#define hydro_pwhash_MEMLIMITBYTES 8
#define hydro_pwhash_HASHBYTES 32
#define hydro_pwhash_SALTBYTES 16
#define hydro_pwhash_PARAMSBYTES \
(hydro_pwhash_HASH_ALGBYTES + hydro_pwhash_THREADSBYTES + hydro_pwhash_OPSLIMITBYTES + \
hydro_pwhash_MEMLIMITBYTES + hydro_pwhash_SALTBYTES + hydro_pwhash_HASHBYTES)
#define hydro_pwhash_ENC_ALG 0x01
#define hydro_pwhash_HASH_ALG 0x01
#define hydro_pwhash_CONTEXT "hydro_pw"
static int
_hydro_pwhash_hash(uint8_t out[hydro_random_SEEDBYTES], size_t h_len,
const uint8_t salt[hydro_pwhash_SALTBYTES], const char *passwd,
size_t passwd_len, const char ctx[hydro_pwhash_CONTEXTBYTES],
const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES], uint64_t opslimit,
size_t memlimit, uint8_t threads)
{
_hydro_attr_aligned_(16) uint8_t state[gimli_BLOCKBYTES];
hydro_hash_state h_st;
uint8_t tmp64_u8[8];
uint64_t i;
uint8_t tmp8;
COMPILER_ASSERT(hydro_pwhash_MASTERKEYBYTES >= hydro_hash_KEYBYTES);
hydro_hash_init(&h_st, ctx, master_key);
STORE64_LE(tmp64_u8, (uint64_t) passwd_len);
hydro_hash_update(&h_st, tmp64_u8, sizeof tmp64_u8);
hydro_hash_update(&h_st, passwd, passwd_len);
hydro_hash_update(&h_st, salt, hydro_pwhash_SALTBYTES);
tmp8 = hydro_pwhash_HASH_ALG;
hydro_hash_update(&h_st, &tmp8, 1);
hydro_hash_update(&h_st, &threads, 1);
STORE64_LE(tmp64_u8, (uint64_t) memlimit);
hydro_hash_update(&h_st, tmp64_u8, sizeof tmp64_u8);
STORE64_LE(tmp64_u8, (uint64_t) h_len);
hydro_hash_update(&h_st, tmp64_u8, sizeof tmp64_u8);
hydro_hash_final(&h_st, (uint8_t *) (void *) &state, sizeof state);
gimli_core_u8(state, 1);
COMPILER_ASSERT(gimli_RATE >= 8);
for (i = 0; i < opslimit; i++) {
mem_zero(state, gimli_RATE);
STORE64_LE(state, i);
gimli_core_u8(state, 0);
}
mem_zero(state, gimli_RATE);
COMPILER_ASSERT(hydro_random_SEEDBYTES == gimli_CAPACITY);
memcpy(out, state + gimli_RATE, hydro_random_SEEDBYTES);
hydro_memzero(state, sizeof state);
return 0;
}
void
hydro_pwhash_keygen(uint8_t master_key[hydro_pwhash_MASTERKEYBYTES])
{
hydro_random_buf(master_key, hydro_pwhash_MASTERKEYBYTES);
}
int
hydro_pwhash_deterministic(uint8_t *h, size_t h_len, const char *passwd, size_t passwd_len,
const char ctx[hydro_pwhash_CONTEXTBYTES],
const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES], uint64_t opslimit,
size_t memlimit, uint8_t threads)
{
uint8_t seed[hydro_random_SEEDBYTES];
COMPILER_ASSERT(sizeof zero >= hydro_pwhash_SALTBYTES);
COMPILER_ASSERT(sizeof zero >= hydro_pwhash_MASTERKEYBYTES);
(void) memlimit;
if (_hydro_pwhash_hash(seed, h_len, zero, passwd, passwd_len, ctx, master_key, opslimit,
memlimit, threads) != 0) {
return -1;
}
hydro_random_buf_deterministic(h, h_len, seed);
hydro_memzero(seed, sizeof seed);
return 0;
}
int
hydro_pwhash_create(uint8_t stored[hydro_pwhash_STOREDBYTES], const char *passwd, size_t passwd_len,
const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES], uint64_t opslimit,
size_t memlimit, uint8_t threads)
{
uint8_t *const enc_alg = &stored[0];
uint8_t *const secretbox = &enc_alg[hydro_pwhash_ENC_ALGBYTES];
uint8_t *const hash_alg = &secretbox[hydro_secretbox_HEADERBYTES];
uint8_t *const threads_u8 = &hash_alg[hydro_pwhash_HASH_ALGBYTES];
uint8_t *const opslimit_u8 = &threads_u8[hydro_pwhash_THREADSBYTES];
uint8_t *const memlimit_u8 = &opslimit_u8[hydro_pwhash_OPSLIMITBYTES];
uint8_t *const salt = &memlimit_u8[hydro_pwhash_MEMLIMITBYTES];
uint8_t *const h = &salt[hydro_pwhash_SALTBYTES];
COMPILER_ASSERT(hydro_pwhash_STOREDBYTES >= hydro_pwhash_ENC_ALGBYTES +
hydro_secretbox_HEADERBYTES +
hydro_pwhash_PARAMSBYTES);
(void) memlimit;
mem_zero(stored, hydro_pwhash_STOREDBYTES);
*enc_alg = hydro_pwhash_ENC_ALG;
*hash_alg = hydro_pwhash_HASH_ALG;
*threads_u8 = threads;
STORE64_LE(opslimit_u8, opslimit);
STORE64_LE(memlimit_u8, (uint64_t) memlimit);
hydro_random_buf(salt, hydro_pwhash_SALTBYTES);
COMPILER_ASSERT(sizeof zero >= hydro_pwhash_MASTERKEYBYTES);
if (_hydro_pwhash_hash(h, hydro_pwhash_HASHBYTES, salt, passwd, passwd_len,
hydro_pwhash_CONTEXT, zero, opslimit, memlimit, threads) != 0) {
return -1;
}
COMPILER_ASSERT(hydro_pwhash_MASTERKEYBYTES == hydro_secretbox_KEYBYTES);
return hydro_secretbox_encrypt(secretbox, hash_alg, hydro_pwhash_PARAMSBYTES,
(uint64_t) *enc_alg, hydro_pwhash_CONTEXT, master_key);
}
static int
_hydro_pwhash_verify(uint8_t computed_h[hydro_pwhash_HASHBYTES],
const uint8_t stored[hydro_pwhash_STOREDBYTES], const char *passwd,
size_t passwd_len, const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES],
uint64_t opslimit_max, size_t memlimit_max, uint8_t threads_max)
{
const uint8_t *const enc_alg = &stored[0];
const uint8_t *const secretbox = &enc_alg[hydro_pwhash_ENC_ALGBYTES];
uint8_t params[hydro_pwhash_PARAMSBYTES];
uint8_t *const hash_alg = &params[0];
uint8_t *const threads_u8 = &hash_alg[hydro_pwhash_HASH_ALGBYTES];
uint8_t *const opslimit_u8 = &threads_u8[hydro_pwhash_THREADSBYTES];
uint8_t *const memlimit_u8 = &opslimit_u8[hydro_pwhash_OPSLIMITBYTES];
uint8_t *const salt = &memlimit_u8[hydro_pwhash_MEMLIMITBYTES];
uint8_t *const h = &salt[hydro_pwhash_SALTBYTES];
uint64_t opslimit;
size_t memlimit;
uint8_t threads;
(void) memlimit;
if (*enc_alg != hydro_pwhash_ENC_ALG) {
return -1;
}
if (hydro_secretbox_decrypt(params, secretbox,
hydro_secretbox_HEADERBYTES + hydro_pwhash_PARAMSBYTES,
(uint64_t) *enc_alg, hydro_pwhash_CONTEXT, master_key) != 0) {
return -1;
}
if (*hash_alg != hydro_pwhash_HASH_ALG || (opslimit = LOAD64_LE(opslimit_u8)) > opslimit_max ||
(memlimit = (size_t) LOAD64_LE(memlimit_u8)) > memlimit_max ||
(threads = *threads_u8) > threads_max) {
return -1;
}
if (_hydro_pwhash_hash(computed_h, hydro_pwhash_HASHBYTES, salt, passwd, passwd_len,
hydro_pwhash_CONTEXT, zero, opslimit, memlimit, threads) == 0 &&
hydro_equal(computed_h, h, hydro_pwhash_HASHBYTES) == 1) {
return 0;
}
return -1;
}
int
hydro_pwhash_verify(const uint8_t stored[hydro_pwhash_STOREDBYTES], const char *passwd,
size_t passwd_len, const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES],
uint64_t opslimit_max, size_t memlimit_max, uint8_t threads_max)
{
uint8_t computed_h[hydro_pwhash_HASHBYTES];
int ret;
ret = _hydro_pwhash_verify(computed_h, stored, passwd, passwd_len, master_key, opslimit_max,
memlimit_max, threads_max);
hydro_memzero(computed_h, sizeof computed_h);
return ret;
}
int
hydro_pwhash_derive_static_key(uint8_t *static_key, size_t static_key_len,
const uint8_t stored[hydro_pwhash_STOREDBYTES], const char *passwd,
size_t passwd_len, const char ctx[hydro_pwhash_CONTEXTBYTES],
const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES],
uint64_t opslimit_max, size_t memlimit_max, uint8_t threads_max)
{
uint8_t computed_h[hydro_pwhash_HASHBYTES];
if (_hydro_pwhash_verify(computed_h, stored, passwd, passwd_len, master_key, opslimit_max,
memlimit_max, threads_max) != 0) {
hydro_memzero(computed_h, sizeof computed_h);
return -1;
}
COMPILER_ASSERT(hydro_kdf_CONTEXTBYTES <= hydro_pwhash_CONTEXTBYTES);
COMPILER_ASSERT(hydro_kdf_KEYBYTES <= hydro_pwhash_HASHBYTES);
hydro_kdf_derive_from_key(static_key, static_key_len, 0, ctx, computed_h);
hydro_memzero(computed_h, sizeof computed_h);
return 0;
}
int
hydro_pwhash_reencrypt(uint8_t stored[hydro_pwhash_STOREDBYTES],
const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES],
const uint8_t new_master_key[hydro_pwhash_MASTERKEYBYTES])
{
uint8_t *const enc_alg = &stored[0];
uint8_t *const secretbox = &enc_alg[hydro_pwhash_ENC_ALGBYTES];
uint8_t *const params = &secretbox[hydro_secretbox_HEADERBYTES];
if (*enc_alg != hydro_pwhash_ENC_ALG) {
return -1;
}
if (hydro_secretbox_decrypt(secretbox, secretbox,
hydro_secretbox_HEADERBYTES + hydro_pwhash_PARAMSBYTES,
(uint64_t) *enc_alg, hydro_pwhash_CONTEXT, master_key) != 0) {
return -1;
}
memmove(params, secretbox, hydro_pwhash_PARAMSBYTES);
return hydro_secretbox_encrypt(secretbox, params, hydro_pwhash_PARAMSBYTES, (uint64_t) *enc_alg,
hydro_pwhash_CONTEXT, new_master_key);
}
int
hydro_pwhash_upgrade(uint8_t stored[hydro_pwhash_STOREDBYTES],
const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES], uint64_t opslimit,
size_t memlimit, uint8_t threads)
{
uint8_t *const enc_alg = &stored[0];
uint8_t *const secretbox = &enc_alg[hydro_pwhash_ENC_ALGBYTES];
uint8_t *const params = &secretbox[hydro_secretbox_HEADERBYTES];
uint8_t *const hash_alg = &params[0];
uint8_t *const threads_u8 = &hash_alg[hydro_pwhash_HASH_ALGBYTES];
uint8_t *const opslimit_u8 = &threads_u8[hydro_pwhash_THREADSBYTES];
uint8_t *const memlimit_u8 = &opslimit_u8[hydro_pwhash_OPSLIMITBYTES];
uint8_t *const salt = &memlimit_u8[hydro_pwhash_MEMLIMITBYTES];
uint8_t *const h = &salt[hydro_pwhash_SALTBYTES];
_hydro_attr_aligned_(16) uint8_t state[gimli_BLOCKBYTES];
uint64_t i;
uint64_t opslimit_prev;
if (*enc_alg != hydro_pwhash_ENC_ALG) {
return -1;
}
if (hydro_secretbox_decrypt(secretbox, secretbox,
hydro_secretbox_HEADERBYTES + hydro_pwhash_PARAMSBYTES,
(uint64_t) *enc_alg, hydro_pwhash_CONTEXT, master_key) != 0) {
return -1;
}
memmove(params, secretbox, hydro_pwhash_PARAMSBYTES);
opslimit_prev = LOAD64_LE(opslimit_u8);
if (*hash_alg != hydro_pwhash_HASH_ALG) {
mem_zero(stored, hydro_pwhash_STOREDBYTES);
return -1;
}
COMPILER_ASSERT(hydro_random_SEEDBYTES == gimli_CAPACITY);
memcpy(state + gimli_RATE, h, hydro_random_SEEDBYTES);
for (i = opslimit_prev; i < opslimit; i++) {
mem_zero(state, gimli_RATE);
STORE64_LE(state, i);
gimli_core_u8(state, 0);
}
mem_zero(state, gimli_RATE);
memcpy(h, state + gimli_RATE, hydro_random_SEEDBYTES);
*threads_u8 = threads;
STORE64_LE(opslimit_u8, opslimit);
STORE64_LE(memlimit_u8, (uint64_t) memlimit);
return hydro_secretbox_encrypt(secretbox, params, hydro_pwhash_PARAMSBYTES, (uint64_t) *enc_alg,
hydro_pwhash_CONTEXT, master_key);
}
+162
View File
@@ -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();
}
+63
View File
@@ -0,0 +1,63 @@
#include <Arduino.h>
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)
{
}
+35
View File
@@ -0,0 +1,35 @@
#if defined(CH32V30x_D8) || defined(CH32V30x_D8C)
# include <ch32v30x_rng.h>
#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;
}
+22
View File
@@ -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;
}
+23
View File
@@ -0,0 +1,23 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/* 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;
}
+32
View File
@@ -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 <esp_system.h>
#endif
#ifdef ARDUINO
# include <Arduino.h>
#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;
}
@@ -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;
}
+44
View File
@@ -0,0 +1,44 @@
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/entropy.h>
#if defined(MBEDTLS_ENTROPY_C)
static int
hydro_random_init(void)
{
mbedtls_entropy_context entropy;
uint16_t pos = 0;
mbedtls_entropy_init(&entropy);
// Pull data directly out of the entropy pool for the state, as it's small enough.
if (mbedtls_entropy_func(&entropy, (uint8_t *) &hydro_random_context.counter,
sizeof hydro_random_context.counter) != 0) {
return -1;
}
// mbedtls_entropy_func can't provide more than MBEDTLS_ENTROPY_BLOCK_SIZE in one go.
// This constant depends of mbedTLS configuration (whether the PRNG is backed by SHA256/SHA512
// at this time) Therefore, if necessary, we get entropy multiple times.
do {
const uint8_t dataLeftToConsume = gimli_BLOCKBYTES - pos;
const uint8_t currentChunkSize = (dataLeftToConsume > MBEDTLS_ENTROPY_BLOCK_SIZE)
? MBEDTLS_ENTROPY_BLOCK_SIZE
: dataLeftToConsume;
// Forces mbedTLS to fetch fresh entropy, then get some to feed libhydrogen.
if (mbedtls_entropy_gather(&entropy) != 0 ||
mbedtls_entropy_func(&entropy, &hydro_random_context.state[pos], currentChunkSize) !=
0) {
return -1;
}
pos += MBEDTLS_ENTROPY_BLOCK_SIZE;
} while (pos < gimli_BLOCKBYTES);
mbedtls_entropy_free(&entropy);
return 0;
}
#else
# error Need an entropy source
#endif
+41
View File
@@ -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 <nrf_soc.h>
static int
hydro_random_init(void)
{
const char ctx[hydro_hash_CONTEXTBYTES] = { 'h', 'y', 'd', 'r', 'o', 'P', 'R', 'G' };
hydro_hash_state st;
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;
}
+26
View File
@@ -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 <Particle.h>
static int
hydro_random_init(void)
{
const char ctx[hydro_hash_CONTEXTBYTES] = { 'h', 'y', 'd', 'r', 'o', 'P', 'R', 'G' };
hydro_hash_state st;
uint16_t ebits = 0;
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;
}
+35
View File
@@ -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_ */
+10
View File
@@ -0,0 +1,10 @@
#include <random.h>
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;
}
+38
View File
@@ -0,0 +1,38 @@
#include <hw_rng.h>
#include <rtthread.h>
#define DBG_TAG "libhydrogen"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
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;
}
+63
View File
@@ -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
+85
View File
@@ -0,0 +1,85 @@
#include <errno.h>
#include <fcntl.h>
#ifdef __linux__
# include <poll.h>
#endif
#include <sys/types.h>
#include <unistd.h>
#ifdef __linux__
static int
hydro_random_block_on_dev_random(void)
{
struct pollfd pfd;
int fd;
int pret;
fd = open("/dev/random", O_RDONLY);
if (fd == -1) {
return 0;
}
pfd.fd = fd;
pfd.events = POLLIN;
pfd.revents = 0;
do {
pret = poll(&pfd, 1, -1);
} while (pret < 0 && (errno == EINTR || errno == EAGAIN));
if (pret != 1) {
(void) close(fd);
errno = EIO;
return -1;
}
return close(fd);
}
#endif
static ssize_t
hydro_random_safe_read(const int fd, void *const buf_, size_t len)
{
unsigned char *buf = (unsigned char *) buf_;
ssize_t readnb;
do {
while ((readnb = read(fd, buf, len)) < (ssize_t) 0 && (errno == EINTR || errno == EAGAIN)) {
}
if (readnb < (ssize_t) 0) {
return readnb;
}
if (readnb == (ssize_t) 0) {
break;
}
len -= (size_t) readnb;
buf += readnb;
} while (len > (ssize_t) 0);
return (ssize_t) (buf - (unsigned char *) buf_);
}
static int
hydro_random_init(void)
{
uint8_t tmp[gimli_BLOCKBYTES + 8];
int fd;
int ret = -1;
#ifdef __linux__
if (hydro_random_block_on_dev_random() != 0) {
return -1;
}
#endif
do {
fd = open("/dev/urandom", O_RDONLY);
if (fd == -1 && errno != EINTR) {
return -1;
}
} while (fd == -1);
if (hydro_random_safe_read(fd, tmp, sizeof tmp) == (ssize_t) sizeof tmp) {
memcpy(hydro_random_context.state, tmp, gimli_BLOCKBYTES);
memcpy(&hydro_random_context.counter, tmp + gimli_BLOCKBYTES, 8);
hydro_memzero(tmp, sizeof tmp);
ret = 0;
}
ret |= close(fd);
return ret;
}
+12
View File
@@ -0,0 +1,12 @@
#include <unistd.h>
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;
}
+20
View File
@@ -0,0 +1,20 @@
#include <windows.h>
#define RtlGenRandom SystemFunction036
#if defined(__cplusplus)
extern "C"
#endif
BOOLEAN NTAPI
RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength);
#pragma comment(lib, "advapi32.lib")
static int
hydro_random_init(void)
{
if (!RtlGenRandom((PVOID) hydro_random_context.state,
(ULONG) sizeof hydro_random_context.state)) {
return -1;
}
hydro_random_context.counter = ~LOAD64_LE(hydro_random_context.state);
return 0;
}
+13
View File
@@ -0,0 +1,13 @@
#include <zephyr/random/random.h>
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;
}
+236
View File
@@ -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;
}
+207
View File
@@ -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;
}
+386
View File
@@ -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]);
}
}
+10
View File
@@ -0,0 +1,10 @@
architectures=avr,nrf52,particle-photon,particle-electron,particle-p1,esp32
author=Frank Denis <libhydrogen@pureftpd.org>
category=Other
includes=hydrogen.h
maintainer=Frank Denis <libhydrogen@pureftpd.org>
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
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

+67
View File
@@ -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,
)
+493
View File
@@ -0,0 +1,493 @@
#ifdef NDEBUG
# undef NDEBUG
#endif
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "hydrogen.h"
static const char *ctx = "libtests";
static int
streq(const char *expected, const char *found)
{
if (strcmp(expected, found) != 0) {
fprintf(stderr, "Found: [%s]\n", found);
return 0;
}
return 1;
}
#define assert_streq(EXPECTED, FOUND) assert(streq((EXPECTED), (FOUND)))
static void
test_randombytes(void)
{
uint8_t dk[hydro_random_SEEDBYTES];
uint8_t tmp[10000];
unsigned long b = 0U;
unsigned long bp;
uint32_t x;
size_t i, j;
for (i = 0; i < 10000; i++) {
x = hydro_random_u32();
for (j = 0; j < sizeof x; j++) {
b += (x >> j) & 1;
}
}
assert(b > 18000 && b < 22000);
b = 0;
hydro_random_buf(tmp, sizeof tmp);
for (i = 0; i < 10000; i++) {
for (j = 0; j < sizeof tmp[0]; j++) {
b += (tmp[i] >> j) & 1;
}
}
assert(b > 4500 && b < 5500);
memcpy(dk, tmp, sizeof dk);
b = 0;
hydro_random_buf_deterministic(tmp, 10000, dk);
for (i = 0; i < 10000; i++) {
for (j = 0; j < sizeof tmp[0]; j++) {
b += (tmp[i] >> j) & 1;
}
}
assert(b > 4500 && b < 5500);
bp = b;
b = 0;
hydro_random_buf_deterministic(tmp, 10000, dk);
for (i = 0; i < 10000; i++) {
for (j = 0; j < sizeof tmp[0]; j++) {
b += (tmp[i] >> j) & 1;
}
}
assert(b == bp);
for (i = 0; i < 1000; i++) {
for (j = 1; j < 100; j++) {
x = hydro_random_uniform((uint32_t) j);
assert(x < j);
}
}
}
static void
test_hash(void)
{
hydro_hash_state st;
uint8_t dk[hydro_random_SEEDBYTES];
uint8_t h[100];
uint8_t key[hydro_hash_KEYBYTES];
#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;
}
+8
View File
@@ -0,0 +1,8 @@
{
"files": [ "../tests/tests.c", "../hydrogen.c" ],
"filesystem": {
"files": [ { "name": "/dev/urandom", "from": "urandom_1" } ]
},
"val-timeout": 10800,
"no-results": "true"
}
@@ -0,0 +1,4 @@
{
"machdep": "gcc_ppc_32",
"compilation_cmd": "-I../. -U__SSE2__ -Dvolatile= -D__BYTE_ORDER__=__ORDER_BIG_ENDIAN__ -U__SIZEOF_INT128__"
}
@@ -0,0 +1,4 @@
{
"machdep": "gcc_ppc_64",
"compilation_cmd": "-I../. -U__SSE2__ -Dvolatile= -D__BYTE_ORDER__=__ORDER_BIG_ENDIAN__"
}
@@ -0,0 +1,4 @@
{
"machdep": "gcc_x86_32",
"compilation_cmd": "-I../. -U__SSE2__ -Dvolatile="
}
@@ -0,0 +1,4 @@
{
"machdep": "gcc_x86_64",
"compilation_cmd": "-I../. -U__SSE2__ -Dvolatile="
}
Binary file not shown.
+123
View File
@@ -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"
}
]
File diff suppressed because it is too large Load Diff
+295
View File
@@ -0,0 +1,295 @@
static const char *licenses = R"LICENSE(DearImgui
The MIT License (MIT)
Copyright (c) 2014-2026 Omar Cornut
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
STB
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
Inter Font Family
Copyright (c) 2016 The Inter Project Authors (https://github.com/rsms/inter)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION AND CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
---
Font-Awesome Font Family
In the Font Awesome Free download, the SIL OFL license applies to all icons
packaged as web and desktop font files.
Copyright (c) 2026 Fonticons, Inc. (https://fontawesome.com)
with Reserved Font Name: "Font Awesome".
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
SIL OPEN FONT LICENSE
Version 1.1 - 26 February 2007
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting in part or in whole any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
---
GLFW
Copyright (c) 2002-2006 Marcus Geelnard
Copyright (c) 2006-2019 Camilla Löwy
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would
be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
---
Volk
Copyright (c) 2018-2026 Arseny Kapoulkine
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
)LICENSE";
+103
View File
@@ -0,0 +1,103 @@
#include "localization.h"
const char *localized_strings_storage[LANGUAGE_COUNT][LOCALIZED_STRING_COUNT] = {
[L_GERMAN] = {
[LS_LANGUAGE_NAME] = "Deutsch",
[LS_JANUARY] = "Januar",
[LS_FEBRUARY] = "Februar",
[LS_MARCH] = "März",
[LS_APRIL] = "April",
[LS_MAY] = "Mai",
[LS_JUNE] = "Juni",
[LS_JULY] = "Juli",
[LS_AUGUST] = "August",
[LS_SEPTEMBER] = "September",
[LS_OCTOBER] = "Oktober",
[LS_NOVEMBER] = "November",
[LS_DECEMBER] = "Dezember",
[LS_FILE] = "Datei",
[LS_OPEN] = "Öffnen",
[LS_SAVE] = "Speichern",
[LS_SAVE_AS] = "Speichern unter",
[LS_CLOSE_CALENDAR] = "Kalender schließen###CloseCalendar",
[LS_CLOSE_AND_LOAD_CALENDAR] = "Kalender schließen und laden###CloseLoadCalendar",
[LS_EXIT] = "Beenden###Close",
[LS_VIEW] = "Ansicht",
[LS_LEGEND] = "Legende",
[LS_LANGUAGE] = "Sprache",
[LS_HELP] = "Hilfe",
[LS_DEMO] = "Demo",
[LS_WIKI] = "Wiki",
[LS_ABOUT] = "Über###About",
[LS_CALENDAR_WEEK_SHORT] = "KW",
[LS_MONDAY_SHORT] = "Mo",
[LS_TUESDAY_SHORT] = "Di",
[LS_WEDNESDAY_SHORT] = "Mi",
[LS_THURSDAY_SHORT] = "Do",
[LS_FRIDAY_SHORT] = "Fr",
[LS_SATURDAY_SHORT] = "Sa",
[LS_SUNDAY_SHORT] = "So",
[LS_TITLE_ENTER_PASSWORD] = "Passwort eingeben###Save",
[LS_PASSWORD] = "Passwort",
[LS_CONFIRM_PASSWORD] = "Passwort bestätigen",
[LS_OK] = "Ok",
[LS_CANCEL] = "Abbrechen",
[LS_TITLE_OPEN_PASSWORD] = "Passwort eingeben###Open",
[LS_QUESTION_DO_YOU_REALLY_WANT_TO_QUIT] = "Es gibt noch offene Änderungen.\nMöchten Sie wirklich beenden?",
[LS_QUESTION_DO_YOU_REALLY_WANT_TO_CLOSE_THE_CALENDAR] = "Es gibt noch offene Änderungen.\nMöchten Sie wirklich den Kalender schließen?",
[LS_YES] = "Ja",
[LS_NO] = "Nein",
[LS_CLOSE] = "Schließen",
},
[L_ENGLISH] = {
[LS_LANGUAGE_NAME] = "English",
[LS_JANUARY] = "January",
[LS_FEBRUARY] = "February",
[LS_MARCH] = "March",
[LS_APRIL] = "April",
[LS_MAY] = "May",
[LS_JUNE] = "June",
[LS_JULY] = "July",
[LS_AUGUST] = "August",
[LS_SEPTEMBER] = "September",
[LS_OCTOBER] = "October",
[LS_NOVEMBER] = "November",
[LS_DECEMBER] = "December",
[LS_FILE] = "File",
[LS_OPEN] = "Open",
[LS_SAVE] = "Save",
[LS_SAVE_AS] = "Save as",
[LS_CLOSE_CALENDAR] = "Close Calendar###CloseCalendar",
[LS_CLOSE_AND_LOAD_CALENDAR] = "Close and Load Calendar###CloseLoadCalendar",
[LS_EXIT] = "Exit",
[LS_VIEW] = "View",
[LS_LEGEND] = "Legend",
[LS_LANGUAGE] = "Language",
[LS_HELP] = "Help",
[LS_DEMO] = "Demo",
[LS_WIKI] = "Wiki",
[LS_ABOUT] = "About###About",
[LS_CALENDAR_WEEK_SHORT] = "No.",
[LS_MONDAY_SHORT] = "Mo",
[LS_TUESDAY_SHORT] = "Tu",
[LS_WEDNESDAY_SHORT] = "We",
[LS_THURSDAY_SHORT] = "Th",
[LS_FRIDAY_SHORT] = "Fr",
[LS_SATURDAY_SHORT] = "Sa",
[LS_SUNDAY_SHORT] = "Su",
[LS_TITLE_ENTER_PASSWORD] = "Enter Password###Save",
[LS_PASSWORD] = "Password",
[LS_CONFIRM_PASSWORD] = "Confirm Password",
[LS_OK] = "Ok",
[LS_CANCEL] = "Cancel",
[LS_TITLE_OPEN_PASSWORD] = "Enter Password###Open",
[LS_QUESTION_DO_YOU_REALLY_WANT_TO_QUIT] = "There are unsaved changes.\nDo you really want to quit?",
[LS_QUESTION_DO_YOU_REALLY_WANT_TO_CLOSE_THE_CALENDAR] = "There are unsaved changes.\nDo you really want to close the calendar?",
[LS_YES] = "Yes",
[LS_NO] = "No",
[LS_CLOSE] = "Close",
},
[L_FRENCH] = {
[LS_LANGUAGE_NAME] = "Français",
}
};
+69
View File
@@ -0,0 +1,69 @@
#pragma once
enum Localized_String {
LS_JANUARY,
LS_FEBRUARY,
LS_MARCH,
LS_APRIL,
LS_MAY,
LS_JUNE,
LS_JULY,
LS_AUGUST,
LS_SEPTEMBER,
LS_OCTOBER,
LS_NOVEMBER,
LS_DECEMBER,
LS_LANGUAGE_NAME,
LS_FILE,
LS_OPEN,
LS_SAVE,
LS_SAVE_AS,
LS_CLOSE_CALENDAR,
LS_CLOSE_AND_LOAD_CALENDAR,
LS_EXIT,
LS_VIEW,
LS_LEGEND,
LS_LANGUAGE,
LS_HELP,
LS_DEMO,
LS_WIKI,
LS_ABOUT,
LS_CALENDAR_WEEK_SHORT,
LS_MONDAY_SHORT,
LS_TUESDAY_SHORT,
LS_WEDNESDAY_SHORT,
LS_THURSDAY_SHORT,
LS_FRIDAY_SHORT,
LS_SATURDAY_SHORT,
LS_SUNDAY_SHORT,
LS_TITLE_ENTER_PASSWORD,
LS_PASSWORD,
LS_CONFIRM_PASSWORD,
LS_OK,
LS_CANCEL,
LS_TITLE_OPEN_PASSWORD,
//Questions
LS_QUESTION_DO_YOU_REALLY_WANT_TO_QUIT,
LS_QUESTION_DO_YOU_REALLY_WANT_TO_CLOSE_THE_CALENDAR,
LS_YES,
LS_NO,
LS_CLOSE,
LOCALIZED_STRING_COUNT
};
enum Language {
L_GERMAN,
L_ENGLISH,
L_FRENCH,
LANGUAGE_COUNT
};
#ifdef __cplusplus
extern "C" {
#endif
extern const char *localized_strings_storage[LANGUAGE_COUNT][LOCALIZED_STRING_COUNT];
#ifdef __cplusplus
}
#endif
+74 -2
View File
@@ -56,6 +56,8 @@ static ImGui_ImplVulkanH_Window g_MainWindowData;
static uint32_t g_MinImageCount = 2;
static bool g_SwapChainRebuild = false;
static GLFWwindow *window;
static void glfw_error_callback(int error, const char* description)
{
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
@@ -361,7 +363,7 @@ int main(int, char**)
// Create window with Vulkan context
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
GLFWwindow* window = glfwCreateWindow(1280, 720, "Work Calendar", nullptr, nullptr);
window = glfwCreateWindow(1600, 900, "Work Calendar", nullptr, nullptr);
if (!glfwVulkanSupported())
{
printf("GLFW: Vulkan Not Supported\n");
@@ -452,7 +454,7 @@ int main(int, char**)
init();
// Main loop
while (!glfwWindowShouldClose(window))
while (!glfwWindowShouldClose(window) && !should_exit)
{
// Poll and handle events (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
@@ -527,3 +529,73 @@ int main(int, char**)
return 0;
}
static int run_command_and_return_exit_code(const char *command) {
FILE *process = popen(command, "re");
if (!process) return -1;
return pclose(process);
}
static char *run_command_and_return_stdout(const char *command, size_t buffer_size) {
FILE *process = popen(command, "re");
if (!process) return NULL;
char *result = (char *)calloc(1, buffer_size);
if (!result) return NULL;
for (char *ptr = result; (result + buffer_size - ptr) > 1 && fgets(ptr, result + buffer_size - ptr, process); ptr = ptr + strlen(ptr));
if (!feof(process)) {
free(result);
pclose(process);
return NULL;
}
if (pclose(process)) {
free(result);
return NULL;
}
return result;
}
char *open_file_dialog() {
char *path = NULL;
if (run_command_and_return_exit_code("zenity --version") == 0) {
path = run_command_and_return_stdout("zenity --title=\"Open Calendar\" --modal --file-selection --file-filter=\"Workcalendar (*.wcl) | *.wcl\"", PATH_MAX);
} else if (run_command_and_return_exit_code("kdialog --version") == 0) {
path = run_command_and_return_stdout("kdialog --title \"Open Calendar\" --getopenfilename . \"Workcalendar(*.wcl)\"", PATH_MAX);
}
if (!path) return NULL;
size_t len = strlen(path);
if (len && path[len-1] == '\n')
path[len-1] = '\0';
return path;
}
char *save_file_dialog() {
char *path = NULL;
if (run_command_and_return_exit_code("zenity --version") == 0) {
path = run_command_and_return_stdout("zenity --title=\"Save Calendar\" --modal --file-selection --save --file-filter=\"Workcalendar (*.wcl) | *.wcl\"", PATH_MAX);
} else if (run_command_and_return_exit_code("kdialog --version") == 0) {
path = run_command_and_return_stdout("kdialog --title \"Save Calendar\" --getsavefilename . \"Workcalendar(*.wcl)\"", PATH_MAX);
}
if (!path) return NULL;
size_t len = strlen(path);
if (len && path[len-1] == '\n')
path[len-1] = '\0';
return path;
}
void set_window_title(const char *title) {
glfwSetWindowTitle(window, title);
}
+82 -1
View File
@@ -22,6 +22,8 @@ static bool g_SwapChainOccluded = false;
static UINT g_ResizeWidth = 0, g_ResizeHeight = 0;
static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr;
static HWND hwnd;
// Forward declarations of helper functions
bool CreateDeviceD3D(HWND hWnd);
void CleanupDeviceD3D();
@@ -32,6 +34,85 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
bool should_exit = false;
bool show_demo_window = false;
void set_window_title(const char *window_title){
SetWindowTextA(hwnd, window_title);
}
char *open_file_dialog() {
uint32_t file_name_buffer_size = 4096;
void *file_name_buffer = calloc(1, file_name_buffer_size);
OPENFILENAMEA open_info = {
.lStructSize = sizeof(OPENFILENAMEA),
.hwndOwner = hwnd,
.hInstance = NULL,
.lpstrFilter = "Workcalendar (.wcl)\0*.wcl\0\0",
.lpstrCustomFilter = NULL,
.nMaxCustFilter = 0,
.nFilterIndex = 0,
.lpstrFile = (char *) file_name_buffer,
.nMaxFile = file_name_buffer_size,
.lpstrFileTitle = NULL,
.nMaxFileTitle = NULL,
.lpstrInitialDir = NULL,
.lpstrTitle = "Kalender öffnen",
.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_LONGNAMES ,
.nFileOffset = 0,
.nFileExtension = 0,
.lpstrDefExt = "wcl",
.lCustData = NULL,
.lpfnHook = NULL,
.lpTemplateName = NULL,
};
if(!GetOpenFileNameA(&open_info)) {
free(file_name_buffer);
return NULL;
}
return (char *) file_name_buffer;
}
char *save_file_dialog() {
uint32_t file_name_buffer_size = 4096;
void *file_name_buffer = calloc(1, file_name_buffer_size);
if (save_file_path) {
memcpy(file_name_buffer, save_file_path, strlen(save_file_path));
}
OPENFILENAMEA open_info = {
.lStructSize = sizeof(OPENFILENAMEA),
.hwndOwner = hwnd,
.hInstance = NULL,
.lpstrFilter = "Workcalendar (.wcl)\0*.wcl\0\0",
.lpstrCustomFilter = NULL,
.nMaxCustFilter = 0,
.nFilterIndex = 0,
.lpstrFile = (char *) file_name_buffer,
.nMaxFile = file_name_buffer_size,
.lpstrFileTitle = NULL,
.nMaxFileTitle = NULL,
.lpstrInitialDir = NULL,
.lpstrTitle = "Kalender speichern unter",
.Flags = OFN_LONGNAMES | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST,
.nFileOffset = 0,
.nFileExtension = 0,
.lpstrDefExt = "wcl",
.lCustData = NULL,
.lpfnHook = NULL,
.lpTemplateName = NULL,
};
if(!GetSaveFileNameA(&open_info)) {
free(file_name_buffer);
return NULL;
}
return (char *) file_name_buffer;
}
// Main code
int main(int, char**)
{
@@ -42,7 +123,7 @@ int main(int, char**)
// Create application window
WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr };
::RegisterClassExW(&wc);
HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Work Calendar", WS_OVERLAPPEDWINDOW, 100, 100, (int)(1600 * main_scale), (int)(900 * main_scale), nullptr, nullptr, wc.hInstance, nullptr);
hwnd = ::CreateWindowW(wc.lpszClassName, L"Work Calendar", WS_OVERLAPPEDWINDOW, 100, 100, (int)(1600 * main_scale), (int)(900 * main_scale), nullptr, nullptr, wc.hInstance, nullptr);
// Initialize Direct3D
if (!CreateDeviceD3D(hwnd))
+451 -76
View File
@@ -3,24 +3,33 @@
#include <time.h>
#include <stdio.h>
#include <stdint.h>
#include <hydrogen.h>
#include "localization.h"
#include "inter.h"
#include "inter-bold.h"
#include "font-awesome-solid.h"
#include "licenses.cpp"
extern bool should_exit;
extern bool show_demo_window;
//standard year
static int year = 2025;
static int year;
static int current_year = 2025;
static int current_month = 1;
static int current_day = 1;
static int current_year;
static int current_month;
static int current_day;
//fonts
static ImFont *inter_regular;
static ImFont *inter_bold;
static ImFont *font_awesome;
char *open_file_dialog();
char *save_file_dialog();
void set_window_title(const char *title);
int get_current_year(){
time_t t = time(NULL);
@@ -62,17 +71,69 @@ int days_per_month(int year, int month){
return days_in_month[month - 1];
}
#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];
static Language current_language = L_GERMAN;
static Language selected_language = L_GERMAN;
const char *get_localized_string(Localized_String localized_string) {
if(localized_strings_storage[current_language][localized_string]) {
return localized_strings_storage[current_language][localized_string];
}
return localized_strings_storage[L_ENGLISH][localized_string];
}
static Localized_String month_names[] = { LS_JANUARY, LS_FEBRUARY, LS_MARCH, LS_APRIL, LS_MAY, LS_JUNE, LS_JULY, LS_AUGUST, LS_SEPTEMBER, LS_OCTOBER, LS_NOVEMBER, LS_DECEMBER };
static char *save_file_path = NULL;
static const char *wiki_url = "https://gitea.ammerhai.com/Ammerhai/work-calendar/wiki";
static ImGuiID close_popup;
static ImGuiID close_calendar_popup;
static ImGuiID close_and_load_calendar_popup;
static ImGuiID save_password_popup;
static ImGuiID open_password_popup;
static ImGuiID about_popup;
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);
font_awesome = io.Fonts->AddFontFromMemoryCompressedTTF(font_awesome_solid_compressed_data, font_awesome_solid_compressed_size, 16.0f);
year = get_current_year();
save_file_path = (char *)calloc(1, 1);
ImGuiSettingsHandler settings_handler = {};
settings_handler.TypeName = "Calendar";
settings_handler.TypeHash = ImHashStr("Calendar");
settings_handler.ReadOpenFn = [](ImGuiContext *context, ImGuiSettingsHandler *handler, const char *name) -> void * {
return (void *)-1;
};
settings_handler.ReadLineFn = [](ImGuiContext *context, ImGuiSettingsHandler *handler, void *entry, const char *line) {
if (strncmp(line, "language", sizeof("language") - 1) == 0) {
for (int i = 0; i < LANGUAGE_COUNT; i++) {
if (strncmp(line + sizeof("language"), localized_strings_storage[i][LS_LANGUAGE_NAME], strlen(localized_strings_storage[i][LS_LANGUAGE_NAME])) == 0)
selected_language = (Language)i;
}
}
};
settings_handler.WriteAllFn = [](ImGuiContext *context, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buffer) {
buffer->append("[Calendar][Settings]\n");
buffer->appendf("language=%s\n\n", get_localized_string(LS_LANGUAGE_NAME));
};
ImGui::AddSettingsHandler(&settings_handler);
}
static const char *savefile_path = "./calendar";
static const char *wiki_url = "https://gitea.ammerhai.com/Ammerhai/work-calendar/wiki";
static const char *month_names[] = {"Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"};
typedef struct Category {
bool editing;
@@ -99,66 +160,206 @@ 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)];
static bool unsaved_changes;
static bool last_frame_unsaved_changes;
static char *category_name_before_editing = NULL;
static bool about_open;
void save(){
if (!save_file_path) return;
uint32_t version = 1;
FILE *save_file = fopen(savefile_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);
unsaved_changes = false;
}
void interaction_save_as(){
char *new_save_file_path = save_file_dialog();
if (new_save_file_path) {
if (save_file_path) free(save_file_path);
save_file_path = new_save_file_path;
ImGui::OpenPopup(save_password_popup);
}
}
void load(){
FILE *save_file = fopen(savefile_path, "rb");
FILE *save_file = fopen(save_file_path, "rb");
size_t num_bytes = fread(encrypted_buffer, 1, sizeof(encrypted_buffer), save_file);
fclose (save_file);
if ( num_bytes <= 0) {
return; //TODO Popup warning
}
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));
#define read_from_buffer(x) memcpy(&(x), plaintext_buffer + offset, sizeof(x)); offset += sizeof(x);
size_t offset = 0;
uint32_t version = 0;
fread(&version, sizeof(version), 1, save_file);
read_from_buffer(version);
if (version != 1) {
return; //TODO Popup warning
}
fread(&num_categories, sizeof(num_categories), 1, save_file);
fread(&num_categorized_days, sizeof(num_categorized_days), 1, save_file);
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
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);
unsaved_changes = false;
}
fclose (save_file);
void interaction_open(){
if(unsaved_changes) {
ImGui::OpenPopup(close_and_load_calendar_popup);
} else {
char *new_save_file_path = open_file_dialog();
if (new_save_file_path) {
if (save_file_path) free(save_file_path);
save_file_path = new_save_file_path;
ImGui::OpenPopup(open_password_popup);
}
}
}
void close_calendar(){
if (save_file_path) free(save_file_path);
save_file_path = (char *)calloc(1, 1);
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");
unsaved_changes = false;
}
void interaction_close_calendar() {
if(unsaved_changes) {
ImGui::OpenPopup(close_calendar_popup);
} else {
close_calendar();
}
}
void per_frame(){
current_language = selected_language;
close_popup = ImGui::GetID("###Close");
close_calendar_popup = ImGui::GetID("###CloseCalendar");
close_and_load_calendar_popup = ImGui::GetID("###CloseLoadCalendar");
save_password_popup = ImGui::GetID("###Save");
open_password_popup = ImGui::GetID("###Open");
about_popup = ImGui::GetID("###About");
if (ImGui::IsKeyDown(ImGuiMod_Ctrl) && ImGui::IsKeyPressed(ImGuiKey_S, false)) {
if (ImGui::IsKeyDown(ImGuiMod_Shift)) {
interaction_save_as();
} else if (strlen(save_file_path)) {
save();
}
}
if (ImGui::IsKeyDown(ImGuiMod_Ctrl) && ImGui::IsKeyPressed(ImGuiKey_O, false)) {
interaction_open();
}
if (ImGui::IsKeyDown(ImGuiMod_Ctrl) && ImGui::IsKeyPressed(ImGuiKey_X, false)) {
interaction_close_calendar();
}
if (last_frame_unsaved_changes != unsaved_changes) {
char *title = (char *)calloc(1, strlen("Work Calendar - ") + strlen(save_file_path) + 2);
memcpy(title, "Work Calendar - ", strlen("Work Calendar - "));
memcpy(title + strlen(title), save_file_path, strlen(save_file_path));
if(unsaved_changes)
memcpy(title + strlen(title), "*", 1);
set_window_title(title);
free(title);
last_frame_unsaved_changes = unsaved_changes;
}
ImGuiStyle &style = ImGui::GetStyle();
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
year_min_size = ImGui::CalcTextSize("8888").x + 5;
@@ -176,69 +377,209 @@ 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");
if (ImGui::BeginMainMenuBar()) {
if (ImGui::BeginMenu("Datei")) {
if (ImGui::MenuItem("Kalender öffnen", NULL)) {
//TODO popup + filepicker
load();
if (ImGui::BeginMenu(get_localized_string(LS_FILE))) {
if (ImGui::MenuItem(get_localized_string(LS_OPEN), "Ctrl + O")) {
interaction_open();
}
if (ImGui::MenuItem("Kalender speichern", NULL)) {
if (ImGui::MenuItem(get_localized_string(LS_SAVE), "Ctrl + S", false, strlen(save_file_path))) {
save();
}
ImGui::Separator();
ImGui::MenuItem("Abmelden", NULL);
if (ImGui::MenuItem(get_localized_string(LS_SAVE_AS), "Ctrl + Shift + S")) {
interaction_save_as();
}
ImGui::Separator();
if (ImGui::MenuItem("Beenden", NULL)) {
if (ImGui::MenuItem(get_localized_string(LS_CLOSE_CALENDAR), "Ctrl + X")) {
interaction_close_calendar();
}
ImGui::Separator();
if (ImGui::MenuItem(get_localized_string(LS_EXIT), NULL)) {
if(unsaved_changes) {
ImGui::OpenPopup(close_popup);
} else {
should_exit = true;
}
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Ansicht")) {
ImGui::MenuItem("Legende", NULL, &legend_visible);
if (ImGui::BeginMenu(get_localized_string(LS_VIEW))) {
ImGui::MenuItem(get_localized_string(LS_LEGEND), NULL, &legend_visible);
if (ImGui::BeginMenu(get_localized_string(LS_LANGUAGE))) {
for (int language = 0; language < LANGUAGE_COUNT; language++) {
if (ImGui::MenuItem(localized_strings_storage[language][LS_LANGUAGE_NAME], NULL, current_language == language))
selected_language = (Language) language;
}
ImGui::EndMenu();
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Hilfe")) {
ImGui::MenuItem("Demo", NULL, &show_demo_window);
if (ImGui::MenuItem("Wiki", NULL)) {
if (ImGui::BeginMenu(get_localized_string(LS_HELP))) {
ImGui::MenuItem(get_localized_string(LS_DEMO), NULL, &show_demo_window);
if (ImGui::MenuItem(get_localized_string(LS_WIKI), NULL)) {
ImGui::GetPlatformIO().Platform_OpenInShellFn(ImGui::GetCurrentContext(), wiki_url);
}
ImGui::Separator();
ImGui::MenuItem("Über", NULL);
if (ImGui::MenuItem(get_localized_string(LS_ABOUT), NULL)) {
ImGui::OpenPopup(about_popup); about_open = true;
}
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
// save calendar file with password modal dialogue
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?");
if (ImGui::BeginPopupModal(get_localized_string(LS_TITLE_ENTER_PASSWORD), NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
ImGui::AlignTextToFramePadding();
ImGui::Text("%s", get_localized_string(LS_PASSWORD));
ImGui::SameLine();
ImGui::SetNextItemWidth(-1);
ImGui::InputText("##Password", password_input_buffer, sizeof(password_input_buffer), ImGuiInputTextFlags_Password);
ImGui::AlignTextToFramePadding();
ImGui::Text("%s", get_localized_string(LS_CONFIRM_PASSWORD));
ImGui::SameLine();
ImGui::SetNextItemWidth(-1);
ImGui::InputText("##Confirm_Password", password_confirmation_input_buffer, sizeof(password_confirmation_input_buffer), ImGuiInputTextFlags_Password);
ImGui::PopStyleVar();
if (ImGui::Button(get_localized_string(LS_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(get_localized_string(LS_CANCEL), 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();
}
// open calendar file with password modal dialogue
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal(get_localized_string(LS_TITLE_OPEN_PASSWORD), NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
ImGui::AlignTextToFramePadding();
ImGui::Text("%s", get_localized_string(LS_PASSWORD));
ImGui::SameLine();
ImGui::SetNextItemWidth(-1);
ImGui::InputText("##Passwort", password_input_buffer, sizeof(password_input_buffer), ImGuiInputTextFlags_Password);
ImGui::PopStyleVar();
if (ImGui::Button(get_localized_string(LS_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(get_localized_string(LS_CANCEL), ImVec2(120, 0))) {
close_calendar();
ImGui::CloseCurrentPopup();
hydro_memzero(password_input_buffer, sizeof(password_input_buffer));
}
ImGui::EndPopup();
}
// close and load calendar modal dialogue
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal(get_localized_string(LS_CLOSE_AND_LOAD_CALENDAR), NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("%s", get_localized_string(LS_QUESTION_DO_YOU_REALLY_WANT_TO_CLOSE_THE_CALENDAR));
ImGui::SetItemDefaultFocus();
if (ImGui::Button("Ja", ImVec2(120, 0))) {
if (ImGui::Button(get_localized_string(LS_YES), ImVec2(120, 0))) {
ImGui::CloseCurrentPopup();
close_calendar();
char *new_save_file_path = open_file_dialog();
if (new_save_file_path) {
if (save_file_path) free(save_file_path);
save_file_path = new_save_file_path;
ImGui::OpenPopup(open_password_popup);
}
}
ImGui::SameLine();
if (ImGui::Button(get_localized_string(LS_NO), ImVec2(120, 0))) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
// close calendar modal dialogue
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal(get_localized_string(LS_CLOSE_CALENDAR), NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("%s", get_localized_string(LS_QUESTION_DO_YOU_REALLY_WANT_TO_CLOSE_THE_CALENDAR));
ImGui::SetItemDefaultFocus();
if (ImGui::Button(get_localized_string(LS_YES), ImVec2(120, 0))) {
close_calendar();
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button(get_localized_string(LS_NO), ImVec2(120, 0))) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
// close application modal dialogue
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal(get_localized_string(LS_EXIT), NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("%s", get_localized_string(LS_QUESTION_DO_YOU_REALLY_WANT_TO_QUIT));
ImGui::SetItemDefaultFocus();
if (ImGui::Button(get_localized_string(LS_YES), ImVec2(120, 0))) {
should_exit = true;
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Nein", ImVec2(120, 0))) {
if (ImGui::Button(get_localized_string(LS_NO), ImVec2(120, 0))) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
// about modal dialogue
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
ImGui::SetNextWindowSize(ImVec2(640.0f, 480.0f), ImGuiCond_Appearing);
if (ImGui::BeginPopupModal(get_localized_string(LS_ABOUT), &about_open)) {
ImGui::TextWrapped("%s", licenses);
ImGui::EndPopup();
}
if (ImGui::Begin("Work Calendar", 0)) {
ImGui::PushStyleColor(ImGuiCol_TableHeaderBg, {0,0,0,0});
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, {0,0,0,0});
@@ -278,7 +619,7 @@ void per_frame(){
if (ImGui::BeginTable("Month", 1, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX)) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::TextAligned(0.5, -FLT_MIN, month_names[month - 1]);
ImGui::TextAligned(0.5, -FLT_MIN, get_localized_string(month_names[month - 1]));
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
@@ -287,7 +628,7 @@ void per_frame(){
if (ImGui::BeginTable("CalendarWeek", 1, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_Borders)){
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::TextAligned(0.5, -FLT_MIN, "KW");
ImGui::TextAligned(0.5, -FLT_MIN, get_localized_string(LS_CALENDAR_WEEK_SHORT));
for (int day = 1; day <= days_per_month(year, month); day++) {
int weekday = weekday_from_day(year, month, day);
@@ -297,11 +638,11 @@ void per_frame(){
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) {
int calendar_week = calendar_week_from_day(year, month, day);
if (calendar_week == 0) {
ImGui::TextAligned(0.5, -FLT_MIN, "%d", 53);
} else {
ImGui::TextAligned(0.5, -FLT_MIN, "%d", calender_week);
ImGui::TextAligned(0.5, -FLT_MIN, "%d", calendar_week);
}
}
}
@@ -315,13 +656,13 @@ void per_frame(){
if (ImGui::BeginTable("Weekdays", 7, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_Borders)){
ImGui::TableSetupColumn("Mo");
ImGui::TableSetupColumn("Di");
ImGui::TableSetupColumn("Mi");
ImGui::TableSetupColumn("Do");
ImGui::TableSetupColumn("Fr");
ImGui::TableSetupColumn("Sa");
ImGui::TableSetupColumn("So");
ImGui::TableSetupColumn(get_localized_string(LS_MONDAY_SHORT));
ImGui::TableSetupColumn(get_localized_string(LS_TUESDAY_SHORT));
ImGui::TableSetupColumn(get_localized_string(LS_WEDNESDAY_SHORT));
ImGui::TableSetupColumn(get_localized_string(LS_THURSDAY_SHORT));
ImGui::TableSetupColumn(get_localized_string(LS_FRIDAY_SHORT));
ImGui::TableSetupColumn(get_localized_string(LS_SATURDAY_SHORT));
ImGui::TableSetupColumn(get_localized_string(LS_SUNDAY_SHORT));
ImGui::TableHeadersRow();
ImGui::EndTable();
@@ -335,7 +676,7 @@ void per_frame(){
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++) {
for (int day = 1; day <= days_per_month(year, month); day++) {
int weekday = weekday_from_day(year, month, day);
if (weekday == 0 || day == 1){
@@ -395,6 +736,7 @@ void per_frame(){
//apply selected color to field
if (selected_category != -1 && ImGui::IsItemClicked()) {
unsaved_changes = true;
for (int i = 0; i < num_categorized_days; i++) {
if (categorized_days[i].year != year) continue;
if (categorized_days[i].month != month) continue;
@@ -473,6 +815,10 @@ void per_frame(){
if (category.editing) {
ImGui::SetNextItemWidth(-1);
if (ImGui::InputText("##name", category.name, IM_ARRAYSIZE(category.name), ImGuiInputTextFlags_EnterReturnsTrue)) {
if (strcmp(category_name_before_editing, category.name) != 0) {
unsaved_changes = true;
}
free(category_name_before_editing);
category.editing = false;
}
} else {
@@ -480,23 +826,52 @@ void per_frame(){
}
ImGui::TableSetColumnIndex(2);
if (ImGui::Button("e"))
ImGui::PushFont(font_awesome);
if (ImGui::Button("\uf304")) {
if(!category.editing) {
category_name_before_editing = _strdup(category.name);
} else {
if (strcmp(category_name_before_editing, category.name) != 0) {
unsaved_changes = true;
}
free(category_name_before_editing);
}
category.editing = !category.editing;
}
ImGui::PopFont();
ImGui::TableSetColumnIndex(3);
if (ImGui::Button("d")) {
num_categories--;
ImGui::PushFont(font_awesome);
if (ImGui::Button("\uf1f8")) {
for (size_t j = 0; j < num_categorized_days; j++) {
// Remove categorized days that have the to be deleted category
if (categorized_days[j].category == i) {
for (size_t k = j; k < num_categorized_days - 1; k++) categorized_days[k] = categorized_days[k + 1];
num_categorized_days--;
j--;
}
// Decrement the category index of all categorized days that have an index higher than the one to be removed
if (categorized_days[j].category > i) categorized_days[j].category--;
}
// Remove the category
for (size_t j = i; j < num_categories - 1; j++) categories[j] = categories[j + 1];
num_categories--;
unsaved_changes = true;
}
ImGui::PopFont();
ImGui::PopID();
}
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::PushFont(font_awesome);
if (ImGui::Button("+")) {
categories[num_categories] = {};
num_categories++;
unsaved_changes = true;
}
ImGui::PopFont();
ImGui::EndTable();
}
Binary file not shown.