├── .github └── workflows │ ├── codeql.yml │ └── maven.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── pom.xml └── src ├── main ├── c │ ├── CMakeLists.txt │ ├── chacha20.c │ ├── curve25519.c │ ├── curve25519 │ │ ├── curve25519_tables.h │ │ └── fiat │ │ │ ├── AUTHORS │ │ │ ├── CONTRIBUTORS │ │ │ ├── COPYRIGHT │ │ │ ├── LICENSE-APACHE │ │ │ ├── LICENSE-BSD-1 │ │ │ ├── LICENSE-MIT │ │ │ ├── curve25519_32_le.h │ │ │ ├── curve25519_32_p.h │ │ │ ├── curve25519_64_le.h │ │ │ └── curve25519_64_p.h │ ├── drbg.c │ ├── ed25519.c │ ├── entropy │ │ ├── CMakeLists.txt │ │ ├── entropy_getentropy.c │ │ ├── entropy_getrandom.c │ │ ├── entropy_rdseed64.c │ │ └── entropy_windows.c │ ├── hash.c │ ├── include │ │ ├── chacha20.h │ │ ├── curve25519.h │ │ ├── dev_o1c_lib_O1CLib.h │ │ ├── drbg.h │ │ ├── ed25519.h │ │ ├── hash.h │ │ ├── mem.h │ │ ├── o1c.h │ │ ├── poly1305.h │ │ ├── ristretto255.h │ │ ├── scalar25519.h │ │ ├── sha512.h │ │ ├── util.h │ │ ├── x25519.h │ │ └── xchacha20poly1305.h │ ├── jni.c │ ├── poly1305.c │ ├── poly1305_32.c │ ├── poly1305_64.c │ ├── ristretto255.c │ ├── scalar25519.c │ ├── sha512.c │ ├── util.c │ ├── x25519.c │ └── xchacha20poly1305.c ├── java │ └── dev │ │ └── o1c │ │ ├── KeyManager.java │ │ ├── KeyPair.java │ │ ├── O1CException.java │ │ ├── PublicKey.java │ │ ├── SecretKey.java │ │ ├── impl │ │ ├── DefaultKeyManager.java │ │ ├── DefaultKeyPair.java │ │ ├── DefaultPublicKey.java │ │ ├── DefaultSecretKey.java │ │ ├── blake3 │ │ │ ├── Blake3Hash.java │ │ │ ├── Blake3HashFactory.java │ │ │ ├── Blake3RandomBytesGenerator.java │ │ │ ├── ChunkState.java │ │ │ ├── Constants.java │ │ │ ├── Flag.java │ │ │ └── Output.java │ │ └── chacha20 │ │ │ ├── ChaCha20.java │ │ │ ├── ChaCha20Poly1305Cipher.java │ │ │ ├── Poly1305.java │ │ │ └── XChaCha20Poly1305Cipher.java │ │ ├── internal │ │ ├── SystemRandomBytesGenerator.java │ │ └── SystemSeedGenerator.java │ │ ├── lib │ │ └── O1CLib.java │ │ ├── lwc │ │ ├── ascon │ │ │ ├── Ascon.java │ │ │ ├── AsconCipher.java │ │ │ └── AsconHash.java │ │ ├── package-info.java │ │ └── xoodyak │ │ │ ├── Cyclist.java │ │ │ ├── ExtensibleOutputFunction.java │ │ │ ├── KeyDerivationFunction.java │ │ │ ├── KeyedHash.java │ │ │ ├── Xoodoo.java │ │ │ ├── Xoodyak.java │ │ │ ├── XoodyakCipher.java │ │ │ ├── XoodyakHash.java │ │ │ ├── XoodyakHashFactory.java │ │ │ └── XoodyakRandomBytesGenerator.java │ │ ├── spi │ │ ├── Cipher.java │ │ ├── Hash.java │ │ ├── HashFactory.java │ │ ├── InvalidAuthenticationTagException.java │ │ ├── InvalidKeyException.java │ │ ├── InvalidProviderException.java │ │ ├── InvalidSignatureException.java │ │ ├── RandomBytesGenerator.java │ │ ├── SeedGenerator.java │ │ └── package-info.java │ │ └── util │ │ ├── ByteOps.java │ │ └── Validator.java └── resources │ └── META-INF │ └── services │ ├── dev.o1c.KeyManager │ └── dev.o1c.spi.SeedGenerator └── test ├── c ├── CMakeLists.txt ├── gen.c ├── test.h ├── test_chacha20.c ├── test_chacha20.txt ├── test_curve25519.c ├── test_curve25519.txt ├── test_ed25519.c ├── test_ed25519.txt ├── test_poly1305.c ├── test_ristretto255.c ├── test_ristretto255.txt ├── test_ristretto255b3.c ├── test_scalar25519.c ├── test_scalar25519.txt ├── test_xchacha20poly1305.c └── test_xchacha20poly1305.txt ├── java └── dev │ └── o1c │ ├── KeyManagerTest.java │ ├── impl │ ├── blake3 │ │ └── Blake3HashTest.java │ └── chacha20 │ │ ├── ChaCha20Poly1305CipherTest.java │ │ ├── ChaCha20Test.java │ │ ├── Poly1305Test.java │ │ └── XChaCha20Poly1305CipherTest.java │ ├── lwc │ ├── NistLwcTestVectors.java │ ├── ascon │ │ ├── AsconCipherTest.java │ │ └── AsconHashTest.java │ └── xoodyak │ │ ├── XoodooTest.java │ │ ├── XoodyakCipherTest.java │ │ └── XoodyakHashTest.java │ ├── spi │ ├── CipherTest.java │ └── CryptoHashTest.java │ └── util │ └── ByteOpsTest.java └── resources └── dev └── o1c ├── impl ├── blake3 │ └── test_vectors.json └── chacha20 │ └── xchacha20poly1305.txt.gz └── lwc ├── ascon ├── LWC_AEAD_KAT_128_128.txt.gz └── LWC_HASH_KAT_256.txt.gz └── xoodyak ├── LWC_AEAD_KAT_128_128.txt.gz ├── LWC_HASH_KAT_256.txt.gz └── xoodoo.txt.gz /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [ main ] 9 | schedule: 10 | - cron: '15 4 * * 0' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | language: [ 'cpp', 'java' ] 21 | 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@v2 25 | 26 | # Initializes the CodeQL tools for scanning. 27 | - name: Initialize CodeQL 28 | uses: github/codeql-action/init@v1 29 | with: 30 | languages: ${{ matrix.language }} 31 | # If you wish to specify custom queries, you can do so here or in a config file. 32 | # By default, queries listed here will override any specified in a config file. 33 | # Prefix the list here with "+" to use these queries and those in the config file. 34 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 35 | 36 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 37 | # If this step fails, then you should remove it and run the build manually (see below) 38 | - name: Autobuild 39 | uses: github/codeql-action/autobuild@v1 40 | 41 | # ℹ️ Command-line programs to run using the OS shell. 42 | # 📚 https://git.io/JvXDl 43 | 44 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 45 | # and modify them (or add more) to build your code if your project 46 | # uses a compiled language 47 | 48 | #- run: | 49 | # make bootstrap 50 | # make release 51 | 52 | - name: Perform CodeQL Analysis 53 | uses: github/codeql-action/analyze@v1 54 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Maven 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Set up JDK 15 | uses: actions/setup-java@v1 16 | with: 17 | java-version: 11 18 | - name: Maven Build 19 | run: mvn -B verify 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.iml 3 | .idea/ 4 | *.pem 5 | cmake-build-debug 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(O1C 3 | VERSION 1.0 4 | LANGUAGES C) 5 | 6 | set(CMAKE_C_STANDARD 11) 7 | add_compile_options(-O3 -Wall -Wextra) 8 | 9 | add_subdirectory(src/main/c) 10 | enable_testing() 11 | add_subdirectory(src/test/c) 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2020, 2021, Matt Sicker 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /src/main/c/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(TestBigEndian) 2 | include(CheckSymbolExists) 3 | include(GenerateExportHeader) 4 | include(FetchContent) 5 | 6 | add_subdirectory(entropy) 7 | 8 | set(O1C_SOURCE_FILES 9 | scalar25519.c curve25519.c ed25519.c x25519.c ristretto255.c 10 | chacha20.c poly1305.c xchacha20poly1305.c 11 | drbg.c $ 12 | util.c hash.c sha512.c 13 | jni.c) 14 | 15 | FetchContent_Declare(blake3 16 | GIT_REPOSITORY https://github.com/BLAKE3-team/BLAKE3.git 17 | GIT_TAG 35aa4259bd37457f15d5070957da4825bf64c838 # tag 0.3.7 18 | ) 19 | FetchContent_GetProperties(blake3) 20 | if (NOT blake3_POPULATED) 21 | FetchContent_Populate(blake3) 22 | endif () 23 | 24 | set(BLAKE3_SOURCE_FILES 25 | ${blake3_SOURCE_DIR}/c/blake3.c 26 | ${blake3_SOURCE_DIR}/c/blake3_dispatch.c 27 | ${blake3_SOURCE_DIR}/c/blake3_portable.c) 28 | set(BLAKE3_ASM_UNIX_FILES 29 | ${blake3_SOURCE_DIR}/c/blake3_avx2_x86-64_unix.S 30 | ${blake3_SOURCE_DIR}/c/blake3_avx512_x86-64_unix.S 31 | ${blake3_SOURCE_DIR}/c/blake3_sse41_x86-64_unix.S 32 | ${blake3_SOURCE_DIR}/c/blake3_sse2_x86-64_unix.S) 33 | # TODO: mscv and mingw asm options 34 | add_library(blake3_sse2 OBJECT ${blake3_SOURCE_DIR}/c/blake3_sse2.c) 35 | target_compile_options(blake3_sse2 PUBLIC -msse2 -fPIC) 36 | add_library(blake3_sse41 OBJECT ${blake3_SOURCE_DIR}/c/blake3_sse41.c) 37 | target_compile_options(blake3_sse41 PUBLIC -msse4.1 -fPIC) 38 | add_library(blake3_avx2 OBJECT ${blake3_SOURCE_DIR}/c/blake3_avx2.c) 39 | target_compile_options(blake3_avx2 PUBLIC -mavx2 -fPIC) 40 | add_library(blake3_avx512 OBJECT ${blake3_SOURCE_DIR}/c/blake3_avx512.c) 41 | target_compile_options(blake3_avx512 PUBLIC -mavx512f -mavx512vl -fPIC) 42 | 43 | add_library(o1c SHARED ${O1C_SOURCE_FILES} 44 | ${BLAKE3_SOURCE_FILES} ${BLAKE3_ASM_UNIX_FILES} 45 | $ $ 46 | $ $) 47 | generate_export_header(o1c) 48 | target_include_directories(o1c PUBLIC include ${blake3_SOURCE_DIR}/c ${PROJECT_BINARY_DIR}/src/main/c) 49 | target_compile_options(o1c PRIVATE 50 | -pedantic -fstack-protector-strong -Wstrict-prototypes -Wno-error=strict-prototypes -fvisibility=hidden) 51 | target_compile_definitions(o1c PRIVATE _DEFAULT_SOURCE _GNU_SOURCE) 52 | 53 | find_package(JNI 1.8 REQUIRED) 54 | target_include_directories(o1c PUBLIC ${JNI_INCLUDE_DIRS}) 55 | #target_link_libraries(o1c PUBLIC ${JNI_LIBRARIES}) 56 | # TODO: https://cmake.org/cmake/help/latest/module/UseJava.html 57 | #find_package(Java 1.8 REQUIRED COMPONENTS Development) 58 | 59 | # compiler definitions 60 | test_big_endian(NATIVE_BIG_ENDIAN) 61 | if (${NATIVE_BIG_ENDIAN}) 62 | add_compile_definitions(o1c PUBLIC NATIVE_BIG_ENDIAN) 63 | else () 64 | add_compile_definitions(o1c PUBLIC NATIVE_LITTLE_ENDIAN) 65 | endif () 66 | 67 | if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "64") 68 | add_compile_definitions(o1c PUBLIC ARCH_WORD_BITS=64) 69 | else () 70 | add_compile_definitions(o1c PUBLIC ARCH_WORD_BITS=32) 71 | endif () 72 | 73 | check_symbol_exists(memset_s string.h HAVE_MEMSET_S) 74 | if (${HAVE_MEMSET_S}) 75 | add_compile_definitions(o1c PUBLIC HAVE_MEMSET_S) 76 | endif () 77 | -------------------------------------------------------------------------------- /src/main/c/curve25519/fiat/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of fiat-crypto authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS files. 3 | # See the latter for an explanation. 4 | 5 | # Names should be added to this file as one of 6 | # Organization's name 7 | # Individual's name 8 | # Individual's name 9 | # See CONTRIBUTORS for the meaning of multiple email addresses. 10 | 11 | # Please keep the list sorted. 12 | 13 | Andres Erbsen 14 | Google Inc. 15 | Jade Philipoom 16 | Massachusetts Institute of Technology 17 | Zoe Paraskevopoulou -------------------------------------------------------------------------------- /src/main/c/curve25519/fiat/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This is the official list of people have contributed code to the 2 | # fiat-crypto repository. 3 | # 4 | # The AUTHORS file lists the copyright holders; this file 5 | # lists people. For example, Google employees are listed here 6 | # but not in AUTHORS, because Google holds the copyright. 7 | # 8 | # When adding J Random Contributor's name to this file, 9 | # either J's name or J's organization's name should be 10 | # added to the AUTHORS file, depending on who holds the copyright. 11 | # 12 | # Names should be added to this file like so: 13 | # Individual's name 14 | # Individual's name 15 | # 16 | # An entry with multiple email addresses specifies that the 17 | # first address should be used in the submit logs and 18 | # that the other addresses should be recognized as the 19 | # same person. 20 | 21 | # Please keep the list sorted. 22 | 23 | Adam Chlipala 24 | Andres Erbsen 25 | Daniel Ziegler 26 | David Benjamin 27 | Jade Philipoom 28 | Jason Gross 29 | Robert Sloan 30 | -------------------------------------------------------------------------------- /src/main/c/curve25519/fiat/COPYRIGHT: -------------------------------------------------------------------------------- 1 | SPDX-License-Identifier: MIT OR Apache-2.0 OR BSD-1-Clause 2 | 3 | Fiat Cryptography is licensed under the MIT License or 4 | , the Apache License, Version 2.0 5 | or , or 6 | the BSD 1-Clause License or 7 | , at your option. 8 | -------------------------------------------------------------------------------- /src/main/c/curve25519/fiat/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | The Apache License, Version 2.0 (Apache-2.0) 2 | 3 | Copyright 2015-2020 the fiat-crypto authors (see the AUTHORS file) 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | -------------------------------------------------------------------------------- /src/main/c/curve25519/fiat/LICENSE-BSD-1: -------------------------------------------------------------------------------- 1 | The BSD 1-Clause License (BSD-1-Clause) 2 | 3 | Copyright (c) 2015-2020 the fiat-crypto authors (see the AUTHORS file) 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are 8 | met: 9 | 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 13 | THIS SOFTWARE IS PROVIDED BY the fiat-crypto authors "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, 17 | Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 21 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 22 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /src/main/c/curve25519/fiat/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2020 the fiat-crypto authors (see the AUTHORS file). 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/c/drbg.c: -------------------------------------------------------------------------------- 1 | #include "drbg.h" 2 | #include "blake3.h" 3 | 4 | #include 5 | #include 6 | 7 | static _Thread_local struct { 8 | blake3_hasher st; 9 | uint64_t counter; 10 | bool initialized; 11 | } drbg; 12 | 13 | #define drbg_RESEED_INTERVAL (UINT64_C(1) << 48) 14 | 15 | static void drbg_ratchet(void) { 16 | if (++drbg.counter == drbg_RESEED_INTERVAL) { 17 | drbg_reseed(); 18 | } else { 19 | uint8_t key[BLAKE3_KEY_LEN]; 20 | blake3_hasher_finalize(&drbg.st, key, BLAKE3_KEY_LEN); 21 | blake3_hasher_init_keyed(&drbg.st, key); 22 | } 23 | } 24 | 25 | static void drbg_ensure_init(void) { 26 | if (!drbg.initialized) { 27 | drbg.counter = 0; 28 | uint8_t seed[BLAKE3_KEY_LEN]; 29 | drbg_entropy(seed, BLAKE3_KEY_LEN); 30 | blake3_hasher_init_keyed(&drbg.st, seed); 31 | drbg_ratchet(); 32 | drbg.initialized = true; 33 | } 34 | } 35 | 36 | void drbg_randombytes(void *buf, unsigned long bytes) { 37 | drbg_ensure_init(); 38 | blake3_hasher_finalize_seek(&drbg.st, 64, (uint8_t *) buf, bytes); 39 | drbg_ratchet(); 40 | } 41 | 42 | void drbg_reseed(void) { 43 | drbg.initialized = false; 44 | drbg_ensure_init(); 45 | } 46 | -------------------------------------------------------------------------------- /src/main/c/ed25519.c: -------------------------------------------------------------------------------- 1 | #include "ed25519.h" 2 | #include "curve25519.h" 3 | #include "scalar25519.h" 4 | #include "sha512.h" 5 | #include "drbg.h" 6 | 7 | #include 8 | 9 | void o1c_ed25519_expand_key(o1c_ed25519_expanded_key_t private_key, const o1c_ed25519_seed_t seed) { 10 | uint8_t az[o1c_sha512_HASH_BYTES]; 11 | o1c_sha512(az, seed->v, o1c_ed25519_SEED_BYTES); 12 | 13 | o1c_scalar25519_t as; 14 | o1c_scalar25519_clamp(as, az); 15 | 16 | ge_p3 A; 17 | ge_scalar_mul_base(A, as); 18 | memcpy(private_key->v, seed->v, o1c_ed25519_SEED_BYTES); 19 | ge_ext_serialize(private_key->v + 32, A); 20 | } 21 | 22 | void o1c_ed25519_keypair(o1c_ed25519_public_key_t public_key, o1c_ed25519_expanded_key_t private_key) { 23 | o1c_ed25519_seed_t seed; 24 | drbg_randombytes(seed->v, o1c_ed25519_SEED_BYTES); 25 | o1c_ed25519_expand_key(private_key, seed); 26 | memcpy(public_key->v, private_key->v + 32, 32); 27 | } 28 | 29 | void o1c_ed25519_sign(uint8_t s[o1c_ed25519_SIGN_BYTES], const uint8_t *m, size_t len, 30 | const o1c_ed25519_expanded_key_t key) { 31 | uint8_t az[o1c_sha512_HASH_BYTES]; 32 | o1c_sha512(az, key->v, 32); 33 | 34 | o1c_sha512_ctx_t ctx; 35 | o1c_sha512_init(ctx); 36 | o1c_sha512_update(ctx, az + 32, 32); 37 | o1c_sha512_update(ctx, m, len); 38 | uint8_t nonce[o1c_sha512_HASH_BYTES]; 39 | o1c_sha512_final(ctx, nonce); 40 | o1c_scalar25519_t nonce_r; 41 | o1c_scalar25519_reduce(nonce_r, nonce); 42 | ge_p3 R; 43 | ge_scalar_mul_base(R, nonce_r); 44 | ge_ext_serialize(s, R); 45 | o1c_sha512_init(ctx); 46 | o1c_sha512_update(ctx, s, 32); 47 | o1c_sha512_update(ctx, key->v + 32, 32); 48 | o1c_sha512_update(ctx, m, len); 49 | uint8_t hram[o1c_sha512_HASH_BYTES]; 50 | o1c_sha512_final(ctx, hram); 51 | o1c_scalar25519_t hram_r, result, az_r; 52 | o1c_scalar25519_reduce(hram_r, hram); 53 | o1c_scalar25519_clamp(az_r, az); 54 | o1c_scalar25519_mul_add(result, hram_r, az_r, nonce_r); 55 | memcpy(s + 32, result->v, 32); 56 | } 57 | 58 | bool o1c_ed25519_verify(const uint8_t s[o1c_ed25519_SIGN_BYTES], const uint8_t *m, size_t len, 59 | const o1c_ed25519_public_key_t key) { 60 | ge_p3 A; 61 | if ((s[63] & 224) != 0 || !ge_ext_deserialize_vartime(A, key->v)) { 62 | return false; 63 | } 64 | fe t; 65 | fe_neg(t, A->X); 66 | fe_reduce(A->X, t); 67 | fe_neg(t, A->T); 68 | fe_reduce(A->T, t); 69 | uint8_t pk_copy[o1c_ed25519_PUBLIC_BYTES]; 70 | memcpy(pk_copy, key->v, o1c_ed25519_PUBLIC_BYTES); 71 | uint8_t r_copy[32]; 72 | memcpy(r_copy, s, 32); 73 | #ifdef NATIVE_BIG_ENDIAN 74 | #error "TODO: this needs to be fixed for big endian systems" 75 | #endif 76 | union { 77 | uint64_t u64[4]; 78 | uint8_t u8[32]; 79 | } s_copy; 80 | memcpy(&s_copy.u8[0], s + 32, 32); 81 | static const uint64_t kOrder[4] = { 82 | UINT64_C(0x5812631a5cf5d3ed), 83 | UINT64_C(0x14def9dea2f79cd6), 84 | 0, 85 | UINT64_C(0x1000000000000000), 86 | }; 87 | for (unsigned i = 3;; i--) { 88 | if (s_copy.u64[i] > kOrder[i] || i == 0) return false; 89 | if (s_copy.u64[i] < kOrder[i]) break; 90 | } 91 | o1c_sha512_ctx_t ctx; 92 | o1c_sha512_init(ctx); 93 | o1c_sha512_update(ctx, s, 32); 94 | o1c_sha512_update(ctx, key->v, 32); 95 | o1c_sha512_update(ctx, m, len); 96 | uint8_t hash[o1c_sha512_HASH_BYTES]; 97 | o1c_sha512_final(ctx, hash); 98 | o1c_scalar25519_t h; 99 | o1c_scalar25519_reduce(h, hash); 100 | ge_p2 R; 101 | // TODO: remove need to cast here 102 | ge_double_scalar_mul_vartime(R, h, A, (const o1c_scalar25519_s *) s_copy.u8); 103 | uint8_t r_check[32]; 104 | ge_proj_serialize(r_check, R); 105 | return o1c_mem_eq(r_check, r_copy, 32); 106 | } 107 | -------------------------------------------------------------------------------- /src/main/c/entropy/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(CheckSymbolExists) 2 | include(CheckIncludeFile) 3 | 4 | check_symbol_exists(getentropy unistd.h;sys/random.h HAVE_GETENTROPY) 5 | check_symbol_exists(getrandom sys/random.h HAVE_GETRANDOM) 6 | check_symbol_exists(_rdseed64_step immintrin.h HAVE_RDSEED64) 7 | check_include_file(windows.h HAVE_WINDOWS_H) 8 | 9 | if (${HAVE_GETENTROPY}) 10 | add_library(entropy OBJECT entropy_getentropy.c) 11 | elseif(${HAVE_GETRANDOM}) 12 | add_library(entropy OBJECT entropy_getrandom.c) 13 | elseif(${HAVE_RDSEED64}) 14 | add_library(entropy OBJECT entropy_rdseed64.c) 15 | target_compile_options(entropy PUBLIC -mrdseed) 16 | elseif(${HAVE_WINDOWS_H}) 17 | add_library(entropy OBJECT entropy_windows.c) 18 | endif () 19 | 20 | target_include_directories(entropy PUBLIC ../include) 21 | target_compile_options(entropy PUBLIC -fPIC) 22 | -------------------------------------------------------------------------------- /src/main/c/entropy/entropy_getentropy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void drbg_entropy(void *buf, size_t bytes) { 9 | if (getentropy(buf, bytes) != 0) { 10 | fprintf(stderr, "getentropy() - %s", strerror(errno)); 11 | abort(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/c/entropy/entropy_getrandom.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void drbg_entropy(void *buf, size_t bytes) { 8 | if (getrandom(buf, bytes, 0) == -1) { 9 | fprintf(stderr, "getrandom() - %s", strerror(errno)); 10 | abort(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/c/entropy/entropy_rdseed64.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // https://software.intel.com/content/www/us/en/develop/blogs/the-difference-between-rdrand-and-rdseed.html 5 | static inline void rdseed(uint64_t *seed) { 6 | while (_rdseed64_step(seed) == 0) /* retry */; 7 | } 8 | 9 | void drbg_entropy(void *buf, size_t bytes) { 10 | while (bytes > 8) { 11 | rdseed((uint64_t *) buf); 12 | buf += 8; 13 | bytes -= 8; 14 | } 15 | uint64_t seed; 16 | rdseed(&seed); 17 | memcpy(buf, &seed, bytes); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/c/entropy/entropy_windows.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define RtlGenRandom SystemFunction036 5 | 6 | BOOLEAN NTAPI 7 | RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength); 8 | #pragma comment(lib, "advapi32.lib") 9 | 10 | void drbg_entropy(void *buf, size_t bytes) { 11 | RtlGenRandom((PVOID) buf, (ULONG) bytes); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/c/hash.c: -------------------------------------------------------------------------------- 1 | #include "hash.h" 2 | #include "util.h" 3 | 4 | inline void o1c_hash_init(o1c_hash_t ctx) { 5 | blake3_hasher *state = (blake3_hasher *) ctx->state; 6 | blake3_hasher_init(state); 7 | } 8 | 9 | inline void o1c_hash_key_setup(o1c_hash_t ctx, const uint8_t k[o1c_hash_KEY_BYTES]) { 10 | blake3_hasher *state = (blake3_hasher *) ctx->state; 11 | blake3_hasher_init_keyed(state, k); 12 | } 13 | 14 | inline void o1c_hash_kdf_setup(o1c_hash_t ctx, const char *context) { 15 | blake3_hasher *state = (blake3_hasher *) ctx->state; 16 | blake3_hasher_init_derive_key(state, context); 17 | } 18 | 19 | inline void o1c_hash_update(o1c_hash_t ctx, const uint8_t *m, unsigned long bytes) { 20 | blake3_hasher *state = (blake3_hasher *) ctx->state; 21 | blake3_hasher_update(state, m, bytes); 22 | } 23 | 24 | inline void o1c_hash_final(o1c_hash_t ctx, uint8_t *out, unsigned long out_bytes) { 25 | blake3_hasher *state = (blake3_hasher *) ctx->state; 26 | blake3_hasher_finalize(state, out, out_bytes); 27 | } 28 | 29 | inline void o1c_hash(uint8_t *out, unsigned long out_bytes, const uint8_t *in, unsigned long in_bytes) { 30 | o1c_hash_t ctx; 31 | o1c_hash_init(ctx); 32 | o1c_hash_update(ctx, in, in_bytes); 33 | o1c_hash_final(ctx, out, out_bytes); 34 | o1c_bzero(ctx, sizeof(blake3_hasher)); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/c/include/chacha20.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "o1c_export.h" 6 | 7 | #define o1c_chacha20_KEY_BYTES 32 8 | #define o1c_chacha20_NONCE_BYTES 12 9 | #define o1c_hchacha20_KEY_BYTES 32 10 | #define o1c_hchacha20_NONCE_BYTES 16 11 | 12 | typedef struct o1c_chacha20_s { 13 | uint32_t state[16]; 14 | } o1c_chacha20_s, o1c_chacha20_t[1]; 15 | 16 | O1C_EXPORT void o1c_chacha20_key_setup(o1c_chacha20_t ctx, const uint8_t k[o1c_chacha20_KEY_BYTES]); 17 | 18 | O1C_EXPORT void o1c_chacha20_nonce_setup(o1c_chacha20_t ctx, const uint8_t n[o1c_chacha20_NONCE_BYTES]); 19 | 20 | O1C_EXPORT void o1c_chacha20_nonce_ic_setup(o1c_chacha20_t ctx, const uint8_t n[o1c_chacha20_NONCE_BYTES], uint32_t ic); 21 | 22 | O1C_EXPORT void o1c_chacha20_bytes(o1c_chacha20_t ctx, uint8_t *c, const uint8_t *p, unsigned long bytes); 23 | 24 | O1C_EXPORT void o1c_chacha20_keystream(o1c_chacha20_t ctx, uint8_t *s, unsigned long bytes); 25 | 26 | O1C_EXPORT void o1c_chacha20_stream(uint8_t *c, unsigned long bytes, const uint8_t n[o1c_chacha20_NONCE_BYTES], 27 | const uint8_t k[o1c_chacha20_KEY_BYTES]); 28 | 29 | O1C_EXPORT void 30 | o1c_chacha20_xor_ic(uint8_t *out, const uint8_t *in, unsigned long bytes, const uint8_t n[o1c_chacha20_NONCE_BYTES], 31 | uint32_t ic, const uint8_t k[o1c_chacha20_KEY_BYTES]); 32 | 33 | O1C_EXPORT void 34 | o1c_chacha20_xor(uint8_t *out, const uint8_t *in, unsigned long bytes, const uint8_t n[o1c_chacha20_NONCE_BYTES], 35 | const uint8_t k[o1c_chacha20_KEY_BYTES]); 36 | 37 | O1C_EXPORT void 38 | o1c_hchacha20(uint8_t sk[o1c_hchacha20_KEY_BYTES], const uint8_t n[o1c_hchacha20_NONCE_BYTES], 39 | const uint8_t k[o1c_hchacha20_KEY_BYTES]); 40 | -------------------------------------------------------------------------------- /src/main/c/include/curve25519.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "scalar25519.h" 4 | 5 | #include 6 | #include 7 | 8 | #if (ARCH_WORD_BITS == 64) 9 | #define fe_LIMBS 5 10 | typedef uint64_t fe_limb_t; 11 | #else 12 | #define fe_LIMBS 10 13 | typedef uint32_t fe_limb_t; 14 | #endif 15 | typedef fe_limb_t fe[fe_LIMBS]; 16 | static_assert(sizeof(fe) == sizeof(fe_limb_t) * fe_LIMBS, "fe_limb_t[fe_LIMBS] does not match fe"); 17 | 18 | void fe_add(fe h, const fe f, const fe g); 19 | 20 | void fe_sub(fe h, const fe f, const fe g); 21 | 22 | void fe_neg(fe h, const fe f); 23 | 24 | void fe_reduce(fe h, const fe f); 25 | 26 | void fe_mul(fe h, const fe f, const fe g); 27 | 28 | void fe_mul121666(fe h, const fe f); 29 | 30 | void fe_sqr(fe h, const fe f); 31 | 32 | void fe_select(fe h, uint8_t b, const fe f, const fe g); 33 | 34 | void fe_cmov(fe f, const fe g, uint8_t b); 35 | 36 | void fe_cswap(fe f, fe g, uint8_t b); 37 | 38 | void fe_deserialize(fe h, const uint8_t s[32]); 39 | 40 | void fe_serialize(uint8_t s[32], const fe f); 41 | 42 | void fe_copy(fe h, const fe f); 43 | 44 | void fe_0(fe f); 45 | 46 | void fe_1(fe f); 47 | 48 | bool fe_is_neg(const fe f); 49 | 50 | bool fe_is_nonzero(const fe f); 51 | 52 | void fe_abs(fe h, const fe f); 53 | 54 | void fe_invert(fe h, const fe f); 55 | 56 | void fe_pow22523(fe h, const fe f); 57 | 58 | typedef struct projective_point { 59 | fe X; 60 | fe Y; 61 | fe Z; 62 | } ge_p2[1]; 63 | 64 | typedef struct extended_point { 65 | fe X; 66 | fe Y; 67 | fe Z; 68 | fe T; 69 | } ge_p3[1]; 70 | 71 | typedef struct completed_point { 72 | fe X; 73 | fe Y; 74 | fe Z; 75 | fe T; 76 | } ge_p1p1[1]; 77 | 78 | typedef struct affine_niels_point { 79 | fe yplusx; 80 | fe yminusx; 81 | fe xy2d; 82 | } ge_precomp[1]; 83 | 84 | typedef struct projective_niels_point { 85 | fe YplusX; 86 | fe YminusX; 87 | fe Z; 88 | fe T2d; 89 | } ge_cached[1]; 90 | 91 | void ge_p2_0(ge_p2 h); 92 | 93 | void ge_p3_0(ge_p3 h); 94 | 95 | void ge_cached_0(ge_cached h); 96 | 97 | void ge_precomp_0(ge_precomp h); 98 | 99 | void ge_proj_serialize(uint8_t s[32], const ge_p2 f); 100 | 101 | void ge_ext_serialize(uint8_t s[32], const ge_p3 f); 102 | 103 | int ge_ext_deserialize_vartime(ge_p3 h, const uint8_t s[32]); 104 | 105 | void ge_ext_to_proj_niels(ge_cached r, const ge_p3 p); 106 | 107 | void ge_comp_to_proj(ge_p2 r, const ge_p1p1 p); 108 | 109 | void ge_comp_to_ext(ge_p3 r, const ge_p1p1 p); 110 | 111 | void ge_ext_add(ge_p1p1 r, const ge_p3 p, const ge_cached q); 112 | 113 | void ge_ext_sub(ge_p1p1 r, const ge_p3 p, const ge_cached q); 114 | 115 | void ge_ext_madd(ge_p1p1 r, const ge_p3 p, const ge_precomp q); 116 | 117 | void ge_ext_msub(ge_p1p1 r, const ge_p3 p, const ge_precomp q); 118 | 119 | void ge_ext_to_proj(ge_p2 r, const ge_p3 p); 120 | 121 | void ge_comp_to_proj_niels(ge_cached r, const ge_p1p1 p); 122 | 123 | void ge_proj_dbl(ge_p1p1 r, const ge_p2 p); 124 | 125 | void ge_ext_dbl(ge_p1p1 r, const ge_p3 p); 126 | 127 | void ge_scalar_mul_base(ge_p3 h, const o1c_scalar25519_t s); 128 | 129 | void ge_scalar_mul(ge_p3 r, const o1c_scalar25519_t s, const ge_p3 q); 130 | 131 | /* 132 | r = a * A + b * B 133 | where a = a[0]+256*a[1]+...+256^31 a[31]. 134 | and b = b[0]+256*b[1]+...+256^31 b[31]. 135 | B is the Ed25519 base point (x,4/5) with x positive. 136 | Only used for signatures verification. 137 | */ 138 | void ge_double_scalar_mul_vartime(ge_p2 r, const o1c_scalar25519_t a, const ge_p3 A, const o1c_scalar25519_t b); 139 | -------------------------------------------------------------------------------- /src/main/c/include/dev_o1c_lib_O1CLib.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class dev_o1c_lib_O1CLib */ 4 | 5 | #ifndef _Included_dev_o1c_lib_O1CLib 6 | #define _Included_dev_o1c_lib_O1CLib 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: dev_o1c_lib_O1CLib 12 | * Method: randomBytes 13 | * Signature: ([B)V 14 | */ 15 | JNIEXPORT void JNICALL Java_dev_o1c_lib_O1CLib_randomBytes 16 | (JNIEnv *, jclass, jbyteArray); 17 | 18 | /* 19 | * Class: dev_o1c_lib_O1CLib 20 | * Method: entropyBytes 21 | * Signature: ([B)V 22 | */ 23 | JNIEXPORT void JNICALL Java_dev_o1c_lib_O1CLib_entropyBytes 24 | (JNIEnv *, jclass, jbyteArray); 25 | 26 | /* 27 | * Class: dev_o1c_lib_O1CLib 28 | * Method: hashStateSize 29 | * Signature: ()I 30 | */ 31 | JNIEXPORT jint JNICALL Java_dev_o1c_lib_O1CLib_hashStateSize 32 | (JNIEnv *, jclass); 33 | 34 | /* 35 | * Class: dev_o1c_lib_O1CLib 36 | * Method: hashInit 37 | * Signature: ([B)V 38 | */ 39 | JNIEXPORT void JNICALL Java_dev_o1c_lib_O1CLib_hashInit 40 | (JNIEnv *, jclass, jbyteArray); 41 | 42 | /* 43 | * Class: dev_o1c_lib_O1CLib 44 | * Method: keyedHashInit 45 | * Signature: ([B[B)V 46 | */ 47 | JNIEXPORT void JNICALL Java_dev_o1c_lib_O1CLib_keyedHashInit 48 | (JNIEnv *, jclass, jbyteArray, jbyteArray); 49 | 50 | /* 51 | * Class: dev_o1c_lib_O1CLib 52 | * Method: kdfHashInit 53 | * Signature: ([B[B)V 54 | */ 55 | JNIEXPORT void JNICALL Java_dev_o1c_lib_O1CLib_kdfHashInit 56 | (JNIEnv *, jclass, jbyteArray, jbyteArray); 57 | 58 | /* 59 | * Class: dev_o1c_lib_O1CLib 60 | * Method: hashUpdate 61 | * Signature: ([B[BII)V 62 | */ 63 | JNIEXPORT void JNICALL Java_dev_o1c_lib_O1CLib_hashUpdate 64 | (JNIEnv *, jclass, jbyteArray, jbyteArray, jint, jint); 65 | 66 | /* 67 | * Class: dev_o1c_lib_O1CLib 68 | * Method: hashFinal 69 | * Signature: ([B[BI)V 70 | */ 71 | JNIEXPORT void JNICALL Java_dev_o1c_lib_O1CLib_hashFinal 72 | (JNIEnv *, jclass, jbyteArray, jbyteArray, jint, jint); 73 | 74 | /* 75 | * Class: dev_o1c_lib_O1CLib 76 | * Method: hash 77 | * Signature: ([BII[BII)V 78 | */ 79 | JNIEXPORT void JNICALL Java_dev_o1c_lib_O1CLib_hash 80 | (JNIEnv *, jclass, jbyteArray, jint, jint, jbyteArray, jint, jint); 81 | 82 | /* 83 | * Class: dev_o1c_lib_O1CLib 84 | * Method: keyedHash 85 | * Signature: ([B[BII[BII)V 86 | */ 87 | JNIEXPORT void JNICALL Java_dev_o1c_lib_O1CLib_keyedHash 88 | (JNIEnv *, jclass, jbyteArray, jbyteArray, jint, jint, jbyteArray, jint, jint); 89 | 90 | /* 91 | * Class: dev_o1c_lib_O1CLib 92 | * Method: scalarFieldBaseMultiply 93 | * Signature: ([B[B)V 94 | */ 95 | JNIEXPORT void JNICALL Java_dev_o1c_lib_O1CLib_scalarFieldBaseMultiply 96 | (JNIEnv *, jclass, jbyteArray, jbyteArray); 97 | 98 | /* 99 | * Class: dev_o1c_lib_O1CLib 100 | * Method: scalarFieldMultiply 101 | * Signature: ([B[B[B)V 102 | */ 103 | JNIEXPORT void JNICALL Java_dev_o1c_lib_O1CLib_scalarFieldMultiply 104 | (JNIEnv *, jclass, jbyteArray, jbyteArray, jbyteArray); 105 | 106 | /* 107 | * Class: dev_o1c_lib_O1CLib 108 | * Method: generateScalarFieldKeyPair 109 | * Signature: ([B[B)V 110 | */ 111 | JNIEXPORT void JNICALL Java_dev_o1c_lib_O1CLib_generateScalarFieldKeyPair 112 | (JNIEnv *, jclass, jbyteArray, jbyteArray); 113 | 114 | /* 115 | * Class: dev_o1c_lib_O1CLib 116 | * Method: authenticatedEncrypt 117 | * Signature: ([B[B[B[BII[BI[BI)V 118 | */ 119 | JNIEXPORT void JNICALL Java_dev_o1c_lib_O1CLib_authenticatedEncrypt 120 | (JNIEnv *, jclass, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jint, jint, jbyteArray, jint, jbyteArray, jint); 121 | 122 | /* 123 | * Class: dev_o1c_lib_O1CLib 124 | * Method: authenticatedDecrypt 125 | * Signature: ([B[B[B[BII[BI[BI)Z 126 | */ 127 | JNIEXPORT jboolean JNICALL Java_dev_o1c_lib_O1CLib_authenticatedDecrypt 128 | (JNIEnv *, jclass, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jint, jint, jbyteArray, jint, jbyteArray, jint); 129 | 130 | /* 131 | * Class: dev_o1c_lib_O1CLib 132 | * Method: deriveKeyPairFromSeed 133 | * Signature: ([B[B[B)V 134 | */ 135 | JNIEXPORT void JNICALL Java_dev_o1c_lib_O1CLib_deriveKeyPairFromSeed 136 | (JNIEnv *, jclass, jbyteArray, jbyteArray, jbyteArray); 137 | 138 | /* 139 | * Class: dev_o1c_lib_O1CLib 140 | * Method: generateSignKeyPair 141 | * Signature: ([B[B)V 142 | */ 143 | JNIEXPORT void JNICALL Java_dev_o1c_lib_O1CLib_generateSignKeyPair 144 | (JNIEnv *, jclass, jbyteArray, jbyteArray); 145 | 146 | /* 147 | * Class: dev_o1c_lib_O1CLib 148 | * Method: sign 149 | * Signature: ([B[BII[BI)V 150 | */ 151 | JNIEXPORT void JNICALL Java_dev_o1c_lib_O1CLib_sign 152 | (JNIEnv *, jclass, jbyteArray, jbyteArray, jint, jint, jbyteArray, jint); 153 | 154 | /* 155 | * Class: dev_o1c_lib_O1CLib 156 | * Method: verify 157 | * Signature: ([B[BII[BI)Z 158 | */ 159 | JNIEXPORT jboolean JNICALL Java_dev_o1c_lib_O1CLib_verify 160 | (JNIEnv *, jclass, jbyteArray, jbyteArray, jint, jint, jbyteArray, jint); 161 | 162 | #ifdef __cplusplus 163 | } 164 | #endif 165 | #endif 166 | -------------------------------------------------------------------------------- /src/main/c/include/drbg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "o1c_export.h" 4 | #include 5 | 6 | // Fills the provided buffer with random bytes. 7 | O1C_EXPORT void drbg_randombytes(void *buf, size_t bytes); 8 | 9 | // Reseeds the current DRBG. 10 | O1C_EXPORT void drbg_reseed(void); 11 | 12 | // Fills the provided buffer with system-provided external entropy. 13 | O1C_EXPORT void drbg_entropy(void *buf, size_t bytes); 14 | -------------------------------------------------------------------------------- /src/main/c/include/ed25519.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "o1c_export.h" 8 | 9 | #define o1c_ed25519_SEED_BYTES 32 10 | #define o1c_ed25519_EXPANDED_BYTES 64 11 | #define o1c_ed25519_PUBLIC_BYTES 32 12 | #define o1c_ed25519_SIGN_BYTES 64 13 | 14 | typedef struct o1c_ed25519_seed_s { 15 | uint8_t v[o1c_ed25519_SEED_BYTES]; 16 | } o1c_ed25519_seed_s, o1c_ed25519_seed_t[1]; 17 | 18 | typedef struct o1c_ed25519_expanded_key_s { 19 | uint8_t v[o1c_ed25519_EXPANDED_BYTES]; 20 | } o1c_ed25519_expanded_key_s, o1c_ed25519_expanded_key_t[1]; 21 | 22 | typedef struct o1c_ed25519_public_key_s { 23 | uint8_t v[o1c_ed25519_PUBLIC_BYTES]; 24 | } o1c_ed25519_public_key_s, o1c_ed25519_public_key_t[1]; 25 | 26 | O1C_EXPORT void o1c_ed25519_expand_key(o1c_ed25519_expanded_key_t private_key, const o1c_ed25519_seed_t seed); 27 | 28 | O1C_EXPORT void o1c_ed25519_keypair(o1c_ed25519_public_key_t public_key, o1c_ed25519_expanded_key_t private_key); 29 | 30 | O1C_EXPORT void 31 | o1c_ed25519_sign(uint8_t s[o1c_ed25519_SIGN_BYTES], const uint8_t *m, size_t len, const o1c_ed25519_expanded_key_t key); 32 | 33 | O1C_EXPORT bool o1c_ed25519_verify(const uint8_t s[o1c_ed25519_SIGN_BYTES], const uint8_t *m, size_t len, 34 | const o1c_ed25519_public_key_t key); 35 | -------------------------------------------------------------------------------- /src/main/c/include/hash.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "o1c_export.h" 4 | #include "blake3.h" 5 | 6 | #include 7 | #include 8 | 9 | #define o1c_hash_KEY_BYTES BLAKE3_KEY_LEN 10 | #define o1c_hash_STATE_BYTES sizeof(blake3_hasher) 11 | 12 | typedef struct o1c_hash_s { 13 | alignas(16) uint8_t state[o1c_hash_STATE_BYTES]; 14 | } o1c_hash_s, o1c_hash_t[1]; 15 | 16 | O1C_EXPORT void o1c_hash_init(o1c_hash_t ctx); 17 | 18 | O1C_EXPORT void o1c_hash_key_setup(o1c_hash_t ctx, const uint8_t k[o1c_hash_KEY_BYTES]); 19 | 20 | O1C_EXPORT void o1c_hash_kdf_setup(o1c_hash_t ctx, const char *context); 21 | 22 | O1C_EXPORT void o1c_hash_update(o1c_hash_t ctx, const uint8_t *m, size_t bytes); 23 | 24 | O1C_EXPORT void o1c_hash_final(o1c_hash_t ctx, uint8_t *out, size_t out_bytes); 25 | 26 | O1C_EXPORT void o1c_hash(uint8_t *out, size_t out_bytes, const uint8_t *in, size_t in_bytes); 27 | -------------------------------------------------------------------------------- /src/main/c/include/mem.h: -------------------------------------------------------------------------------- 1 | #ifndef O1C_MEM_H 2 | #define O1C_MEM_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef __clang__ 10 | # if 100*__clang_major__ + __clang_minor__ > 305 11 | # define UNROLL _Pragma("clang loop unroll(full)") 12 | # endif 13 | #endif 14 | 15 | #ifndef UNROLL 16 | # define UNROLL 17 | #endif 18 | 19 | static inline uint32_t 20 | rotl32(const uint32_t x, const int b) { 21 | return (x << b) | (x >> (32 - b)); 22 | } 23 | 24 | static inline uint64_t 25 | rotl64(const uint64_t x, const int b) { 26 | return (x << b) | (x >> (64 - b)); 27 | } 28 | 29 | static inline uint32_t 30 | rotr32(const uint32_t x, const int b) { 31 | return (x >> b) | (x << (32 - b)); 32 | } 33 | 34 | static inline uint64_t 35 | rotr64(const uint64_t x, const int b) { 36 | return (x >> b) | (x << (64 - b)); 37 | } 38 | 39 | static inline uint64_t 40 | load64_le(const uint8_t src[8]) { 41 | #ifdef NATIVE_LITTLE_ENDIAN 42 | uint64_t w; 43 | memcpy(&w, src, sizeof w); 44 | return w; 45 | #else 46 | uint64_t w = (uint64_t) src[0]; 47 | w |= (uint64_t) src[1] << 8; 48 | w |= (uint64_t) src[2] << 16; 49 | w |= (uint64_t) src[3] << 24; 50 | w |= (uint64_t) src[4] << 32; 51 | w |= (uint64_t) src[5] << 40; 52 | w |= (uint64_t) src[6] << 48; 53 | w |= (uint64_t) src[7] << 56; 54 | return w; 55 | #endif 56 | } 57 | 58 | static inline void 59 | store64_le(uint8_t dst[8], uint64_t w) { 60 | #ifdef NATIVE_LITTLE_ENDIAN 61 | memcpy(dst, &w, sizeof w); 62 | #else 63 | dst[0] = (uint8_t) w; w >>= 8; 64 | dst[1] = (uint8_t) w; w >>= 8; 65 | dst[2] = (uint8_t) w; w >>= 8; 66 | dst[3] = (uint8_t) w; w >>= 8; 67 | dst[4] = (uint8_t) w; w >>= 8; 68 | dst[5] = (uint8_t) w; w >>= 8; 69 | dst[6] = (uint8_t) w; w >>= 8; 70 | dst[7] = (uint8_t) w; 71 | #endif 72 | } 73 | 74 | static inline uint32_t 75 | load32_le(const uint8_t src[4]) { 76 | #ifdef NATIVE_LITTLE_ENDIAN 77 | uint32_t w; 78 | memcpy(&w, src, sizeof w); 79 | return w; 80 | #else 81 | uint32_t w = (uint32_t) src[0]; 82 | w |= (uint32_t) src[1] << 8; 83 | w |= (uint32_t) src[2] << 16; 84 | w |= (uint32_t) src[3] << 24; 85 | return w; 86 | #endif 87 | } 88 | 89 | static inline void 90 | load32_le_n(uint32_t *dst, const uint8_t *src, size_t n) { 91 | UNROLL while (n-- > 0) { 92 | *dst++ = load32_le(src); 93 | src += sizeof(uint32_t); 94 | } 95 | } 96 | 97 | static inline void 98 | store32_le(uint8_t dst[4], uint32_t w) { 99 | #ifdef NATIVE_LITTLE_ENDIAN 100 | memcpy(dst, &w, sizeof w); 101 | #else 102 | dst[0] = (uint8_t) w; w >>= 8; 103 | dst[1] = (uint8_t) w; w >>= 8; 104 | dst[2] = (uint8_t) w; w >>= 8; 105 | dst[3] = (uint8_t) w; 106 | #endif 107 | } 108 | 109 | static inline void 110 | store32_le_n(uint8_t *dst, const uint32_t *src, size_t n) { 111 | UNROLL while (n-- > 0) { 112 | store32_le(dst, *src++); 113 | dst += sizeof(uint32_t); 114 | } 115 | } 116 | 117 | static inline uint64_t 118 | load64_be(const uint8_t src[8]) { 119 | #ifdef NATIVE_BIG_ENDIAN 120 | uint64_t w; 121 | memcpy(&w, src, sizeof w); 122 | return w; 123 | #else 124 | uint64_t w = (uint64_t) src[7]; 125 | w |= (uint64_t) src[6] << 8; 126 | w |= (uint64_t) src[5] << 16; 127 | w |= (uint64_t) src[4] << 24; 128 | w |= (uint64_t) src[3] << 32; 129 | w |= (uint64_t) src[2] << 40; 130 | w |= (uint64_t) src[1] << 48; 131 | w |= (uint64_t) src[0] << 56; 132 | return w; 133 | #endif 134 | } 135 | 136 | static inline void 137 | store64_be(uint8_t dst[8], uint64_t w) { 138 | #ifdef NATIVE_BIG_ENDIAN 139 | memcpy(dst, &w, sizeof w); 140 | #else 141 | dst[7] = (uint8_t) w; 142 | w >>= 8; 143 | dst[6] = (uint8_t) w; 144 | w >>= 8; 145 | dst[5] = (uint8_t) w; 146 | w >>= 8; 147 | dst[4] = (uint8_t) w; 148 | w >>= 8; 149 | dst[3] = (uint8_t) w; 150 | w >>= 8; 151 | dst[2] = (uint8_t) w; 152 | w >>= 8; 153 | dst[1] = (uint8_t) w; 154 | w >>= 8; 155 | dst[0] = (uint8_t) w; 156 | #endif 157 | } 158 | 159 | static inline uint32_t 160 | load32_be(const uint8_t src[4]) { 161 | #ifdef NATIVE_BIG_ENDIAN 162 | uint32_t w; 163 | memcpy(&w, src, sizeof w); 164 | return w; 165 | #else 166 | uint32_t w = (uint32_t) src[3]; 167 | w |= (uint32_t) src[2] << 8; 168 | w |= (uint32_t) src[1] << 16; 169 | w |= (uint32_t) src[0] << 24; 170 | return w; 171 | #endif 172 | } 173 | 174 | static inline void 175 | store32_be(uint8_t dst[4], uint32_t w) { 176 | #ifdef NATIVE_BIG_ENDIAN 177 | memcpy(dst, &w, sizeof w); 178 | #else 179 | dst[3] = (uint8_t) w; 180 | w >>= 8; 181 | dst[2] = (uint8_t) w; 182 | w >>= 8; 183 | dst[1] = (uint8_t) w; 184 | w >>= 8; 185 | dst[0] = (uint8_t) w; 186 | #endif 187 | } 188 | 189 | #endif //O1C_MEM_H 190 | -------------------------------------------------------------------------------- /src/main/c/include/o1c.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | #include "drbg.h" 8 | #include "util.h" 9 | #include "hash.h" 10 | #include "chacha20.h" 11 | #include "poly1305.h" 12 | #include "xchacha20poly1305.h" 13 | #include "sha512.h" 14 | #include "x25519.h" 15 | #include "ed25519.h" 16 | #include "ristretto255.h" 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | -------------------------------------------------------------------------------- /src/main/c/include/poly1305.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "o1c_export.h" 7 | 8 | #define o1c_poly1305_KEY_BYTES 32 9 | #define o1c_poly1305_TAG_BYTES 16 10 | 11 | typedef struct o1c_poly1305_s { 12 | alignas(16) uint8_t state[96]; // large enough for 32-bit and 64-bit state representations 13 | } o1c_poly1305_s, o1c_poly1305_t[1]; 14 | 15 | O1C_EXPORT void o1c_poly1305(uint8_t t[o1c_poly1305_TAG_BYTES], const uint8_t *m, unsigned long bytes, 16 | const uint8_t k[o1c_poly1305_KEY_BYTES]); 17 | 18 | O1C_EXPORT void o1c_poly1305_key_setup(o1c_poly1305_t ctx, const uint8_t k[o1c_poly1305_KEY_BYTES]); 19 | 20 | O1C_EXPORT void o1c_poly1305_update(o1c_poly1305_t ctx, const uint8_t *m, unsigned long bytes); 21 | 22 | O1C_EXPORT void o1c_poly1305_final(o1c_poly1305_t ctx, uint8_t t[o1c_poly1305_TAG_BYTES]); 23 | 24 | void o1c_poly1305_blocks(o1c_poly1305_t ctx, const uint8_t *m, unsigned long bytes); 25 | -------------------------------------------------------------------------------- /src/main/c/include/ristretto255.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "o1c_export.h" 4 | #include "curve25519.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #define o1c_ristretto255_BYTES 32 11 | #define o1c_ristretto255_KEY_BYTES 32 12 | #define o1c_ristretto255_HASH_BYTES 64 13 | #define o1c_ristretto255_SIGN_BYTES 64 14 | 15 | typedef struct o1c_ristretto255_s { 16 | struct extended_point point; 17 | } o1c_ristretto255_s, o1c_ristretto255_t[1]; 18 | 19 | O1C_EXPORT bool o1c_ristretto255_is_canonical(const uint8_t f[o1c_ristretto255_BYTES]); 20 | 21 | O1C_EXPORT bool o1c_ristretto255_deserialize(o1c_ristretto255_t h, const uint8_t f[o1c_ristretto255_BYTES]); 22 | 23 | O1C_EXPORT void o1c_ristretto255_serialize(uint8_t f[o1c_ristretto255_BYTES], const o1c_ristretto255_t p); 24 | 25 | O1C_EXPORT void o1c_ristretto255_elligator(o1c_ristretto255_t h, const uint8_t f[o1c_ristretto255_BYTES]); 26 | 27 | O1C_EXPORT bool o1c_ristretto255_equal(const o1c_ristretto255_t f, const o1c_ristretto255_t g); 28 | 29 | O1C_EXPORT void o1c_ristretto255_from_hash(o1c_ristretto255_t q, const uint8_t h[o1c_ristretto255_HASH_BYTES]); 30 | 31 | O1C_EXPORT bool o1c_ristretto255_scalar_mul(o1c_ristretto255_t q, const o1c_scalar25519_t n, 32 | const o1c_ristretto255_t p); 33 | 34 | O1C_EXPORT bool o1c_ristretto255_scalar_mul_base(o1c_ristretto255_t q, const o1c_scalar25519_t n); 35 | 36 | O1C_EXPORT void o1c_ristretto255b3_derive_pubkey(uint8_t pubkey[o1c_ristretto255_BYTES], 37 | const uint8_t key[o1c_ristretto255_KEY_BYTES]); 38 | 39 | O1C_EXPORT void o1c_ristretto255b3_sign(uint8_t sig[o1c_ristretto255_SIGN_BYTES], const uint8_t *m, size_t m_len, 40 | const uint8_t key[o1c_ristretto255_KEY_BYTES]); 41 | 42 | O1C_EXPORT bool o1c_ristretto255b3_verify(const uint8_t sig[o1c_ristretto255_SIGN_BYTES], const uint8_t *m, size_t m_len, 43 | const uint8_t pubkey[o1c_ristretto255_BYTES]); 44 | -------------------------------------------------------------------------------- /src/main/c/include/scalar25519.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "o1c_export.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define o1c_scalar25519_BYTES 32 10 | #define o1c_scalar25519_NONREDUCED_BYTES 64 11 | 12 | typedef struct o1c_scalar25519_s { 13 | uint8_t v[o1c_scalar25519_BYTES]; 14 | } o1c_scalar25519_s, o1c_scalar25519_t[1]; 15 | 16 | O1C_EXPORT bool o1c_scalar25519_is_canonical(const o1c_scalar25519_t s); 17 | 18 | O1C_EXPORT void o1c_scalar25519_random(o1c_scalar25519_t s); 19 | 20 | O1C_EXPORT void o1c_scalar25519_reduce(o1c_scalar25519_t s, const uint8_t n[o1c_scalar25519_NONREDUCED_BYTES]); 21 | 22 | O1C_EXPORT void o1c_scalar25519_clamp(o1c_scalar25519_t s, const uint8_t n[o1c_scalar25519_BYTES]); 23 | 24 | O1C_EXPORT void o1c_scalar25519_deserialize(o1c_scalar25519_t s, const uint8_t n[o1c_scalar25519_BYTES]); 25 | 26 | O1C_EXPORT void o1c_scalar25519_mul_add(o1c_scalar25519_t s, const o1c_scalar25519_t a, const o1c_scalar25519_t b, 27 | const o1c_scalar25519_t c); 28 | 29 | O1C_EXPORT void o1c_scalar25519_negate(o1c_scalar25519_t neg, const o1c_scalar25519_t s); 30 | -------------------------------------------------------------------------------- /src/main/c/include/sha512.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util.h" 4 | #include "o1c_export.h" 5 | 6 | #include 7 | #include 8 | 9 | typedef struct sha512_ctx_s { 10 | uint64_t state[8]; 11 | uint8_t block[128]; 12 | uint64_t bytes_processed; 13 | } o1c_sha512_ctx_t[1]; 14 | 15 | #define o1c_sha512_HASH_BYTES 64 16 | 17 | O1C_EXPORT void o1c_sha512_init(o1c_sha512_ctx_t ctx); 18 | 19 | O1C_EXPORT void o1c_sha512_update(o1c_sha512_ctx_t ctx, const uint8_t *msg, size_t len); 20 | 21 | O1C_EXPORT void o1c_sha512_final(o1c_sha512_ctx_t ctx, uint8_t *out); 22 | 23 | O1C_EXPORT void o1c_sha512(uint8_t *out, const uint8_t *msg, size_t msg_len); 24 | -------------------------------------------------------------------------------- /src/main/c/include/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "o1c_export.h" 7 | 8 | // Overwrites a buffer with zeros. 9 | O1C_EXPORT void o1c_bzero(void *buf, unsigned long bytes); 10 | 11 | // Returns true in constant time if fst and snd have equal byte contents. 12 | O1C_EXPORT bool o1c_mem_eq(const void *fst, const void *snd, unsigned long bytes); 13 | 14 | // Returns true in constant time if a is all zeros. 15 | O1C_EXPORT bool o1c_is_zero(const void *buf, unsigned long bytes); 16 | 17 | // Converts hex to binary and returns binary length or -1 on error. 18 | O1C_EXPORT long o1c_hex2bin(uint8_t *bin, unsigned long max_bin_len, const char *hex, unsigned long hex_len); 19 | 20 | // Converts binary to hex and returns the hex string. 21 | O1C_EXPORT char *o1c_bin2hex(char *hex, const uint8_t *bin, unsigned long bytes); 22 | 23 | // Calculates optimal padding length for the given unpadded length. 24 | O1C_EXPORT unsigned long o1c_pad_len(unsigned long unpadded_len); 25 | -------------------------------------------------------------------------------- /src/main/c/include/x25519.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "o1c_export.h" 7 | 8 | #define o1c_x25519_SCALAR_BYTES 32 9 | #define o1c_x25519_ELEMENT_BYTES 32 10 | 11 | typedef struct o1c_x25519_scalar_s { 12 | uint8_t v[o1c_x25519_SCALAR_BYTES]; 13 | } o1c_x25519_scalar_s, o1c_x25519_scalar_t[1]; 14 | 15 | typedef struct o1c_x25519_element_s { 16 | uint8_t v[o1c_x25519_ELEMENT_BYTES]; 17 | } o1c_x25519_element_s, o1c_x25519_element_t[1]; 18 | 19 | O1C_EXPORT void o1c_x25519_scalar_random(o1c_x25519_scalar_t s); 20 | 21 | O1C_EXPORT void o1c_x25519_keypair(o1c_x25519_element_t pk, o1c_x25519_scalar_t sk); 22 | 23 | O1C_EXPORT bool 24 | o1c_x25519_scalar_mul(o1c_x25519_element_t q, const o1c_x25519_scalar_t n, const o1c_x25519_element_t p); 25 | 26 | O1C_EXPORT void o1c_x25519_scalar_mul_base(o1c_x25519_element_t q, const o1c_x25519_scalar_t n); 27 | -------------------------------------------------------------------------------- /src/main/c/include/xchacha20poly1305.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "o1c_export.h" 8 | 9 | #define o1c_xchacha20poly1305_KEY_BYTES 32 10 | #define o1c_xchacha20poly1305_NONCE_BYTES 24 11 | #define o1c_xchacha20poly1305_TAG_BYTES 16 12 | 13 | O1C_EXPORT void 14 | o1c_xchacha20poly1305_encrypt(uint8_t *c, uint8_t t[o1c_xchacha20poly1305_TAG_BYTES], const uint8_t *m, 15 | size_t m_len, const uint8_t *ad, size_t ad_len, 16 | const uint8_t n[o1c_xchacha20poly1305_NONCE_BYTES], 17 | const uint8_t k[o1c_xchacha20poly1305_KEY_BYTES]); 18 | 19 | O1C_EXPORT bool 20 | o1c_xchacha20poly1305_decrypt(uint8_t *m, const uint8_t t[o1c_xchacha20poly1305_TAG_BYTES], const uint8_t *c, 21 | size_t c_len, const uint8_t *ad, size_t ad_len, 22 | const uint8_t n[o1c_xchacha20poly1305_NONCE_BYTES], 23 | const uint8_t k[o1c_xchacha20poly1305_KEY_BYTES]); 24 | -------------------------------------------------------------------------------- /src/main/c/poly1305.c: -------------------------------------------------------------------------------- 1 | // adapted from poly1305-donna; public domain or MIT license 2 | #include 3 | 4 | static_assert(ARCH_WORD_BITS == 32 || ARCH_WORD_BITS == 64, "Unsupported architecture"); 5 | 6 | #include "poly1305.h" 7 | #include "util.h" 8 | 9 | #if (ARCH_WORD_BITS == 32) 10 | #define RH_LIMBS 5 11 | #define PAD_LIMBS 4 12 | typedef unsigned long limb_t; 13 | #else 14 | #define RH_LIMBS 3 15 | #define PAD_LIMBS 2 16 | typedef unsigned long long limb_t; 17 | #endif 18 | 19 | #define poly1305_BLOCK_SIZE 16 20 | 21 | typedef struct poly1305_state_internal_s { 22 | limb_t r[RH_LIMBS]; 23 | limb_t h[RH_LIMBS]; 24 | limb_t pad[PAD_LIMBS]; 25 | unsigned long leftover; 26 | unsigned char buffer[poly1305_BLOCK_SIZE]; 27 | unsigned char final; 28 | } poly1305_state_internal_s, poly1305_state_internal_t[1]; 29 | 30 | #if (ARCH_WORD_BITS == 64) 31 | 32 | #include "poly1305_64.c" 33 | 34 | #else 35 | 36 | #include "poly1305_32.c" 37 | 38 | #endif 39 | 40 | void o1c_poly1305(uint8_t t[o1c_poly1305_TAG_BYTES], const uint8_t *m, unsigned long bytes, 41 | const uint8_t k[o1c_poly1305_KEY_BYTES]) { 42 | o1c_poly1305_t ctx; 43 | o1c_poly1305_key_setup(ctx, k); 44 | o1c_poly1305_update(ctx, m, bytes); 45 | o1c_poly1305_final(ctx, t); 46 | o1c_bzero(ctx, sizeof(o1c_poly1305_s)); 47 | } 48 | 49 | void o1c_poly1305_update(o1c_poly1305_t ctx, const uint8_t *m, unsigned long bytes) { 50 | poly1305_state_internal_s *st = (poly1305_state_internal_s *) ctx->state; 51 | unsigned long i; 52 | 53 | /* handle leftover */ 54 | if (st->leftover) { 55 | unsigned long want = (poly1305_BLOCK_SIZE - st->leftover); 56 | if (want > bytes) 57 | want = bytes; 58 | for (i = 0; i < want; i++) 59 | st->buffer[st->leftover + i] = m[i]; 60 | bytes -= want; 61 | m += want; 62 | st->leftover += want; 63 | if (st->leftover < poly1305_BLOCK_SIZE) 64 | return; 65 | o1c_poly1305_blocks(ctx, st->buffer, poly1305_BLOCK_SIZE); 66 | st->leftover = 0; 67 | } 68 | 69 | /* process full blocks */ 70 | if (bytes >= poly1305_BLOCK_SIZE) { 71 | unsigned long want = (bytes & ~(poly1305_BLOCK_SIZE - 1)); 72 | o1c_poly1305_blocks(ctx, m, want); 73 | m += want; 74 | bytes -= want; 75 | } 76 | 77 | /* store leftover */ 78 | if (bytes) { 79 | for (i = 0; i < bytes; i++) 80 | st->buffer[st->leftover + i] = m[i]; 81 | st->leftover += bytes; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/c/util.c: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef _WIN32 11 | # include 12 | # include 13 | #endif 14 | 15 | #if !defined(__unix__) && (defined(__APPLE__) || defined(__linux__)) 16 | #define __unix__ 1 17 | #endif 18 | 19 | #ifdef __OpenBSD__ 20 | #define HAVE_EXPLICIT_BZERO 1 21 | #elif defined(__GLIBC__) && defined(__GLIBC_PREREQ) && defined(_GNU_SOURCE) 22 | #if __GLIBC_PREREQ(2, 25) 23 | #define HAVE_EXPLICIT_BZERO 1 24 | #endif 25 | #endif 26 | 27 | // no-op assembler instruction that compilers supposedly understand to avoid optimizing away 28 | #define BARRIER(X) __asm__("": "+r"(X):) 29 | 30 | inline bool 31 | o1c_mem_eq(const void *const fst, const void *const snd, const size_t len) { 32 | uint8_t *a = (uint8_t *) fst; 33 | uint8_t *b = (uint8_t *) snd; 34 | size_t L = (size_t) len; 35 | BARRIER(a); 36 | BARRIER(b); 37 | BARRIER(L); 38 | unsigned char d = 0U; 39 | for (size_t i = 0U; i < L; ++i) { 40 | uint8_t ai = a[i], bi = b[i]; 41 | BARRIER(ai); 42 | BARRIER(bi); 43 | d |= ai ^ bi; 44 | } 45 | return (d - 1U) >> 8; 46 | } 47 | 48 | inline bool 49 | o1c_is_zero(const void *const a_, const size_t len) { 50 | uint8_t *a = (uint8_t *) a_; 51 | size_t L = (size_t) len; 52 | BARRIER(a); 53 | BARRIER(L); 54 | unsigned char d = 0U; 55 | for (size_t i = 0; i < L; ++i) { 56 | uint8_t ai = a[i]; 57 | BARRIER(ai); 58 | d |= ai; 59 | } 60 | return (d - 1U) >> 8; 61 | } 62 | 63 | inline void 64 | o1c_bzero(void *const buf, size_t bytes) { 65 | #if defined(_WIN32) 66 | SecureZeroMemory(buf, bytes); 67 | #elif defined(HAVE_MEMSET_S) 68 | memset_s(buf, bytes, 0, bytes); 69 | #elif defined(HAVE_EXPLICIT_BZERO) 70 | explicit_bzero(buf, bytes); 71 | #else 72 | uint8_t *b = (uint8_t *) buf; 73 | BARRIER(b); 74 | O1C_UNROLL while (bytes--) *b++ = 0; 75 | BARRIER(b); 76 | #endif 77 | } 78 | 79 | long 80 | o1c_hex2bin(uint8_t *const bin, const unsigned long max_bin_len, const char *const hex, const unsigned long hex_len) { 81 | unsigned long bin_pos = 0U, hex_pos = 0U; 82 | int ret = 0; 83 | unsigned char c, c_alpha0, c_alpha, c_num0, c_num, state = 0U; 84 | uint8_t c_acc = 0U, c_val; 85 | while (hex_pos < hex_len) { 86 | c = (unsigned char) hex[hex_pos]; 87 | c_num = c ^ 48U; 88 | c_num0 = (c_num - 10U) >> 8; 89 | c_alpha = (c & ~32U) - 55U; 90 | c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8; 91 | if ((c_num0 | c_alpha0) == 0U) { 92 | break; 93 | } 94 | c_val = (uint8_t)((c_num0 & c_num) | (c_alpha0 & c_alpha)); 95 | if (bin_pos >= max_bin_len) { 96 | ret = -1; 97 | errno = ERANGE; 98 | break; 99 | } 100 | if (state == 0U) { 101 | c_acc = c_val * 16U; 102 | } else { 103 | bin[bin_pos++] = c_acc | c_val; 104 | } 105 | state = ~state; 106 | hex_pos++; 107 | } 108 | if (state != 0U) { 109 | hex_pos--; 110 | errno = EINVAL; 111 | ret = -1; 112 | } 113 | if (hex_pos != hex_len) { 114 | errno = EINVAL; 115 | ret = -1; 116 | } 117 | if (ret != 0) { 118 | return ret; 119 | } 120 | return (long) bin_pos; 121 | } 122 | 123 | char * 124 | o1c_bin2hex(char *const hex, const uint8_t *const bin, const unsigned long bytes) { 125 | size_t i; 126 | for (i = 0U; i < bytes; ++i) { 127 | int c = bin[i] & 0xf; 128 | int b = bin[i] >> 4; 129 | unsigned int x = (unsigned char) (87U + c + (((c - 10U) >> 8) & ~38U)) << 8 | 130 | (unsigned char) (87U + b + (((b - 10U) >> 8) & ~38U)); 131 | hex[i * 2U] = (char) x; 132 | x >>= 8; 133 | hex[i * 2U + 1U] = (char) x; 134 | } 135 | hex[i * 2U] = 0U; 136 | return hex; 137 | } 138 | 139 | /* 140 | given a length L, we compute the padding length L' as: 141 | 142 | let E = floor(log2(L)) 143 | let S = floor(log2(E)) + 1 144 | let z = E - S 145 | let m = (1 << z) - 1 146 | let L' = (L + m) & ~m 147 | 148 | if you store the pad length encrypted from a separate subkey at the beginning, the rest of the message 149 | can be padded separately (similarly to SSH) 150 | */ 151 | 152 | unsigned long o1c_pad_len(unsigned long unpadded_len) { 153 | double e = floor(log2((double) unpadded_len)); 154 | double s = floor(log2(e)) + 1; 155 | unsigned long z = (unsigned long) (e - s); 156 | unsigned long m = (1 << z) - 1; 157 | unsigned long padded_len = (unpadded_len + m) & ~m; 158 | return padded_len - unpadded_len; 159 | } 160 | -------------------------------------------------------------------------------- /src/main/c/x25519.c: -------------------------------------------------------------------------------- 1 | #include "x25519.h" 2 | #include "curve25519.h" 3 | #include "util.h" 4 | #include "drbg.h" 5 | 6 | void o1c_x25519_scalar_random(o1c_x25519_scalar_t s) { 7 | drbg_randombytes(s->v, o1c_x25519_SCALAR_BYTES); 8 | // inverse clamp; https://tools.ietf.org/html/rfc7748#section-5 specifies that scalars need to be clamped 9 | // when decoded, so we'll make sure they're not clamped in the generated encoded form so that broken implementations 10 | // are deterministically broken. idea from BoringSSL 11 | s->v[0] |= ~248; 12 | s->v[31] &= ~64; 13 | s->v[31] |= ~127; 14 | } 15 | 16 | void o1c_x25519_keypair(o1c_x25519_element_t pk, o1c_x25519_scalar_t sk) { 17 | o1c_x25519_scalar_random(sk); 18 | o1c_x25519_scalar_mul_base(pk, sk); 19 | } 20 | 21 | bool o1c_x25519_scalar_mul(o1c_x25519_element_t q, const o1c_x25519_scalar_t n, const o1c_x25519_element_t p) { 22 | uint8_t swap = 0; 23 | o1c_scalar25519_t t; 24 | o1c_scalar25519_clamp(t, n->v); 25 | 26 | fe x1, x2, z2, x3, z3; 27 | fe_deserialize(x1, p->v); 28 | fe_1(x2); 29 | fe_0(z2); 30 | fe_copy(x3, x1); 31 | fe_1(z3); 32 | 33 | fe tmp0, tmp1; 34 | for (int pos = 254; pos >= 0; --pos) { 35 | uint8_t b = 1 & (t->v[pos / 8] >> (pos & 7)); 36 | swap ^= b; 37 | fe_cswap(x2, x3, swap); 38 | fe_cswap(z2, z3, swap); 39 | swap = b; 40 | fe_sub(tmp0, x3, z3); 41 | fe_sub(tmp1, x2, z2); 42 | fe_add(x2, x2, z2); 43 | fe_add(z2, x3, z3); 44 | fe_mul(z3, tmp0, x2); 45 | fe_mul(z2, z2, tmp1); 46 | fe_sqr(tmp0, tmp1); 47 | fe_sqr(tmp1, x2); 48 | fe_add(x3, z3, z2); 49 | fe_sub(z2, z3, z2); 50 | fe_mul(x2, tmp1, tmp0); 51 | fe_sub(tmp1, tmp1, tmp0); 52 | fe_sqr(z2, z2); 53 | fe_mul121666(z3, tmp1); 54 | fe_sqr(x3, x3); 55 | fe_add(tmp0, tmp0, z3); 56 | fe_mul(z3, x1, z2); 57 | fe_mul(z2, tmp1, tmp0); 58 | } 59 | 60 | fe_cswap(x2, x3, swap); 61 | fe_cswap(z2, z3, swap); 62 | 63 | fe_invert(z2, z2); 64 | fe_mul(x2, x2, z2); 65 | fe_serialize(q->v, x2); 66 | return !o1c_is_zero(q->v, o1c_x25519_ELEMENT_BYTES); 67 | } 68 | 69 | void o1c_x25519_scalar_mul_base(o1c_x25519_element_t q, const o1c_x25519_scalar_t n) { 70 | o1c_scalar25519_t t; 71 | o1c_scalar25519_clamp(t, n->v); 72 | ge_p3 Q; 73 | ge_scalar_mul_base(Q, t); 74 | fe zpy, zmy, zmy_inv; 75 | fe_add(zpy, Q->Z, Q->Y); // Z + Y 76 | fe_sub(zmy, Q->Z, Q->Y); // Z - Y 77 | fe_invert(zmy_inv, zmy); // 1/(Z - Y) 78 | fe_mul(zmy_inv, zpy, zmy_inv); // (Z + Y)/(Z - Y) 79 | fe_serialize(q->v, zmy_inv); 80 | } 81 | -------------------------------------------------------------------------------- /src/main/c/xchacha20poly1305.c: -------------------------------------------------------------------------------- 1 | #include "xchacha20poly1305.h" 2 | #include "chacha20.h" 3 | #include "poly1305.h" 4 | #include "util.h" 5 | #include "mem.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | static const uint8_t pad0[16] = {0}; 12 | 13 | static inline void 14 | init_poly1305(o1c_poly1305_t ctx, const uint8_t *ad, const size_t ad_len, const uint8_t *n, const uint8_t *k) { 15 | uint8_t key[o1c_poly1305_KEY_BYTES]; 16 | o1c_chacha20_stream(key, o1c_poly1305_KEY_BYTES, n, k); 17 | o1c_poly1305_key_setup(ctx, key); 18 | o1c_bzero(key, o1c_poly1305_KEY_BYTES); 19 | o1c_poly1305_update(ctx, ad, ad_len); 20 | o1c_poly1305_update(ctx, pad0, (0x10 - ad_len) & 0xf); 21 | } 22 | 23 | void 24 | o1c_xchacha20poly1305_encrypt(uint8_t *c, uint8_t t[o1c_xchacha20poly1305_TAG_BYTES], const uint8_t *m, 25 | size_t m_len, const uint8_t *ad, size_t ad_len, 26 | const uint8_t n[o1c_xchacha20poly1305_NONCE_BYTES], 27 | const uint8_t k[o1c_xchacha20poly1305_KEY_BYTES]) { 28 | uint8_t sk[o1c_chacha20_KEY_BYTES], sn[o1c_chacha20_NONCE_BYTES] = {0}; 29 | o1c_hchacha20(sk, n, k); 30 | memcpy(sn + 4, n + 16, o1c_chacha20_NONCE_BYTES - 4); 31 | o1c_poly1305_t st; 32 | init_poly1305(st, ad, ad_len, sn, sk); 33 | 34 | o1c_chacha20_xor_ic(c, m, m_len, sn, 1U, sk); 35 | o1c_poly1305_update(st, c, m_len); 36 | o1c_poly1305_update(st, pad0, (0x10 - m_len) & 0xf); 37 | 38 | uint8_t len[8]; 39 | store64_le(len, (uint64_t) ad_len); 40 | o1c_poly1305_update(st, len, sizeof len); 41 | store64_le(len, (uint64_t) m_len); 42 | o1c_poly1305_update(st, len, sizeof len); 43 | 44 | o1c_poly1305_final(st, t); 45 | o1c_bzero(st, sizeof st); 46 | } 47 | 48 | bool 49 | o1c_xchacha20poly1305_decrypt(uint8_t *m, const uint8_t t[o1c_xchacha20poly1305_TAG_BYTES], const uint8_t *c, 50 | size_t c_len, const uint8_t *ad, size_t ad_len, 51 | const uint8_t n[o1c_xchacha20poly1305_NONCE_BYTES], 52 | const uint8_t k[o1c_xchacha20poly1305_KEY_BYTES]) { 53 | uint8_t sk[o1c_chacha20_KEY_BYTES], sn[o1c_chacha20_NONCE_BYTES] = {0}; 54 | o1c_hchacha20(sk, n, k); 55 | memcpy(sn + 4, n + 16, o1c_chacha20_NONCE_BYTES - 4); 56 | o1c_poly1305_t st; 57 | init_poly1305(st, ad, ad_len, sn, sk); 58 | 59 | o1c_poly1305_update(st, c, c_len); 60 | o1c_poly1305_update(st, pad0, (0x10 - c_len) & 0xf); 61 | 62 | uint8_t len[8]; 63 | store64_le(len, (uint64_t) ad_len); 64 | o1c_poly1305_update(st, len, sizeof len); 65 | store64_le(len, (uint64_t) c_len); 66 | o1c_poly1305_update(st, len, sizeof len); 67 | 68 | uint8_t tag[o1c_xchacha20poly1305_TAG_BYTES]; 69 | o1c_poly1305_final(st, tag); 70 | o1c_bzero(st, sizeof st); 71 | 72 | bool ret = o1c_mem_eq(t, tag, o1c_xchacha20poly1305_TAG_BYTES); 73 | if (m == NULL) { 74 | return ret; 75 | } 76 | if (!ret) { 77 | o1c_bzero(m, c_len); 78 | return false; 79 | } 80 | o1c_chacha20_xor_ic(m, c, c_len, sn, 1U, sk); 81 | return true; 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/KeyManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c; 22 | 23 | import dev.o1c.spi.InvalidProviderException; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | import java.util.ServiceLoader; 27 | 28 | /** 29 | * Central management of cryptographic keys. 30 | */ 31 | public interface KeyManager { 32 | 33 | /** 34 | * Obtains an instance of the default KeyManager service. 35 | * 36 | * @return default KeyManager 37 | */ 38 | static KeyManager getInstance() { 39 | for (KeyManager keyManager : ServiceLoader.load(KeyManager.class)) { 40 | return keyManager; 41 | } 42 | throw new InvalidProviderException("No KeyManager services found"); 43 | } 44 | 45 | /** 46 | * Generates a fresh keypair. A keypair consists of a private key and its corresponding public key. These 47 | * are used for {@linkplain KeyPair#sign(byte[]) message signing}, 48 | * {@linkplain KeyPair#box(PublicKey, byte[], byte[]) authenticated public key encryption (box)}, 49 | * {@linkplain KeyPair#sealedBox(PublicKey, byte[], byte[]) authenticated public key signcryption (sealed box)}, 50 | * and their dual methods. 51 | * 52 | * @return fresh keypair 53 | */ 54 | @NotNull KeyPair generateKeyPair(); 55 | 56 | /** 57 | * Generates a fresh secret key. A secret key is used for 58 | * {@linkplain SecretKey#box(byte[], byte[]) authenticated encryption (secret box)}. 59 | * 60 | * @return fresh secret key 61 | */ 62 | @NotNull SecretKey generateSecretKey(); 63 | 64 | /** 65 | * Parses secret key data. 66 | * 67 | * @param secretKey secret key data 68 | * @return parsed secret key 69 | * @throws dev.o1c.spi.InvalidKeyException if provided key is not valid 70 | */ 71 | @NotNull SecretKey parseSecretKey(byte @NotNull [] secretKey); 72 | 73 | /** 74 | * Parses public key data. A public key is used for 75 | * {@linkplain PublicKey#openSignedMessage(byte[]) verifying signed messages} and as the sender or recipient 76 | * parameter in various public key cryptography methods. 77 | * 78 | * @param publicKey public key data 79 | * @return parsed public key 80 | * @throws dev.o1c.spi.InvalidKeyException if provided key is not valid 81 | */ 82 | @NotNull PublicKey parsePublicKey(byte @NotNull [] publicKey); 83 | 84 | /** 85 | * Parses private key data and generates its corresponding public key. 86 | * 87 | * @param privateKey private key data 88 | * @return parsed private key and its corresponding public key 89 | * @throws dev.o1c.spi.InvalidKeyException if provided key is not valid 90 | */ 91 | @NotNull KeyPair parsePrivateKey(byte @NotNull [] privateKey); 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/O1CException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c; 22 | 23 | import java.util.Collection; 24 | 25 | public class O1CException extends RuntimeException { 26 | protected O1CException() { 27 | super(); 28 | } 29 | 30 | public O1CException(String message) { 31 | super(message); 32 | } 33 | 34 | public O1CException(String message, Throwable cause) { 35 | super(message, cause); 36 | } 37 | 38 | public O1CException(Throwable cause) { 39 | super(cause); 40 | } 41 | 42 | public O1CException(String message, Collection suppressedExceptions) { 43 | this(message); 44 | if (suppressedExceptions != null) { 45 | suppressedExceptions.forEach(this::addSuppressed); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/PublicKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c; 22 | 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | /** 26 | * Contains the public part of a {@link KeyPair}. 27 | */ 28 | public interface PublicKey { 29 | 30 | /** 31 | * Opens a signed message and returns its plaintext contents if its signature can be verified with this public key. 32 | * 33 | * @param signedMessage signed message data containing both plaintext and signature 34 | * @return plaintext data if signature matches 35 | * @throws dev.o1c.spi.InvalidSignatureException if the signature does not match or is otherwise invalid 36 | */ 37 | byte @NotNull [] openSignedMessage(byte @NotNull [] signedMessage); 38 | 39 | /** 40 | * Validates the signature of a sealed box created by the given sender to this public key recipient in the given 41 | * context. 42 | * 43 | * @param sender who created the sealed box 44 | * @param sealedBox sealed box data to validate signature 45 | * @param context original context the sealed box was created in 46 | * @throws dev.o1c.spi.InvalidSignatureException if the seal is broken (an invalid signature) 47 | */ 48 | void validateSealedBox(@NotNull PublicKey sender, byte @NotNull [] sealedBox, byte @NotNull [] context); 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/SecretKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c; 22 | 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | public interface SecretKey { 26 | 27 | byte @NotNull [] box(byte @NotNull [] data, byte @NotNull [] context); 28 | 29 | byte @NotNull [] openBox(byte @NotNull [] box, byte @NotNull [] context); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/impl/DefaultKeyManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.impl; 22 | 23 | import cafe.cryptography.curve25519.CompressedRistretto; 24 | import cafe.cryptography.curve25519.Scalar; 25 | import dev.o1c.KeyManager; 26 | import dev.o1c.KeyPair; 27 | import dev.o1c.PublicKey; 28 | import dev.o1c.SecretKey; 29 | import dev.o1c.impl.blake3.Blake3HashFactory; 30 | import dev.o1c.impl.blake3.Blake3RandomBytesGenerator; 31 | import dev.o1c.spi.Hash; 32 | import dev.o1c.spi.InvalidKeyException; 33 | import org.jetbrains.annotations.NotNull; 34 | 35 | import java.util.Arrays; 36 | 37 | public class DefaultKeyManager implements KeyManager { 38 | private static final int KEY_LENGTH = 32; 39 | 40 | @Override 41 | public @NotNull KeyPair generateKeyPair() { 42 | return parsePrivateKey(Blake3RandomBytesGenerator.getInstance().generateBytes(KEY_LENGTH)); 43 | } 44 | 45 | @Override 46 | public @NotNull SecretKey generateSecretKey() { 47 | return new DefaultSecretKey(); 48 | } 49 | 50 | @Override 51 | public @NotNull SecretKey parseSecretKey(byte @NotNull [] secretKey) { 52 | return new DefaultSecretKey(secretKey); 53 | } 54 | 55 | @Override 56 | public @NotNull PublicKey parsePublicKey(byte @NotNull [] publicKey) { 57 | if (publicKey.length != KEY_LENGTH) { 58 | throw new InvalidKeyException("Public key must be 32 bytes"); 59 | } 60 | return new DefaultPublicKey(new CompressedRistretto(publicKey)); 61 | } 62 | 63 | @Override 64 | public @NotNull KeyPair parsePrivateKey(byte @NotNull [] privateKey) { 65 | if (privateKey.length != KEY_LENGTH) { 66 | throw new InvalidKeyException("Private key must be 32 bytes"); 67 | } 68 | Hash expandHash = Blake3HashFactory.INSTANCE.newHash(64); 69 | expandHash.update(privateKey); 70 | byte[] expandedKey = expandHash.doFinalize(); 71 | byte[] lower = Arrays.copyOf(expandedKey, KEY_LENGTH); 72 | byte[] upper = Arrays.copyOfRange(expandedKey, KEY_LENGTH, 64); 73 | lower[0] &= 248; 74 | lower[31] &= 127; 75 | lower[31] |= 64; 76 | Scalar scalar = Scalar.fromBits(lower); 77 | Hash challenge = Blake3HashFactory.INSTANCE.newKeyedHash(upper); 78 | return new DefaultKeyPair(scalar, challenge); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/impl/DefaultSecretKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.impl; 22 | 23 | import dev.o1c.SecretKey; 24 | import dev.o1c.impl.blake3.Blake3RandomBytesGenerator; 25 | import dev.o1c.impl.chacha20.XChaCha20Poly1305Cipher; 26 | import dev.o1c.spi.Cipher; 27 | import org.jetbrains.annotations.NotNull; 28 | 29 | class DefaultSecretKey implements SecretKey { 30 | private final Cipher cipher = new XChaCha20Poly1305Cipher(); 31 | private final byte[] key; 32 | 33 | DefaultSecretKey() { 34 | key = Blake3RandomBytesGenerator.getInstance().generateBytes(cipher.keyLength()); 35 | } 36 | 37 | DefaultSecretKey(byte @NotNull [] key) { 38 | cipher.checkKeyLength(key.length); 39 | this.key = key.clone(); 40 | } 41 | 42 | @Override 43 | public byte @NotNull [] box(byte @NotNull [] data, byte @NotNull [] context) { 44 | // todo: header data? 45 | int nonceLength = cipher.nonceLength(); 46 | int length = data.length; 47 | byte[] secretBox = new byte[length + nonceLength + cipher.tagLength()]; 48 | Blake3RandomBytesGenerator.getInstance().generateBytes(secretBox, 0, nonceLength); 49 | cipher.init(key, secretBox, context); 50 | cipher.encrypt(data, 0, length, secretBox, nonceLength, secretBox, nonceLength + length); 51 | return secretBox; 52 | } 53 | 54 | @Override 55 | public byte @NotNull [] openBox(byte @NotNull [] box, byte @NotNull [] context) { 56 | int nonceLength = cipher.nonceLength(); 57 | byte[] data = new byte[box.length - nonceLength - cipher.tagLength()]; 58 | cipher.init(key, box, context); 59 | cipher.decrypt(box, nonceLength, data.length, box, nonceLength + data.length, data, 0); 60 | return data; 61 | } 62 | 63 | public int nonceLength() { 64 | return cipher.nonceLength(); 65 | } 66 | 67 | public int tagLength() { 68 | return cipher.tagLength(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/impl/blake3/Blake3HashFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.impl.blake3; 22 | 23 | import dev.o1c.spi.Hash; 24 | import dev.o1c.spi.HashFactory; 25 | import dev.o1c.spi.InvalidKeyException; 26 | import dev.o1c.util.ByteOps; 27 | import org.jetbrains.annotations.NotNull; 28 | 29 | /** 30 | * Produces {@link Hash} instances using the BLAKE3 hash function. 31 | */ 32 | public enum Blake3HashFactory implements HashFactory { 33 | /** 34 | * The singleton instance. 35 | */ 36 | INSTANCE; 37 | 38 | /** 39 | * Creates a fresh BLAKE3 hasher in hash mode. 40 | * 41 | * @return new hasher 42 | */ 43 | @Override 44 | public @NotNull Hash newHash() { 45 | return new Blake3Hash(Constants.IV, 0); 46 | } 47 | 48 | /** 49 | * Creates a fresh BLAKE3 hasher in hash mode using the specified default output hash length. 50 | * 51 | * @param hashLength default hash length to use in {@link Hash#doFinalize()} 52 | * @return new hasher 53 | */ 54 | @Override 55 | public @NotNull Hash newHash(int hashLength) { 56 | return new Blake3Hash(Constants.IV, 0, hashLength); 57 | } 58 | 59 | /** 60 | * Creates a fresh BLAKE3 hasher in keyed mode using the provided secret key. 61 | * 62 | * @param key 32-byte secret key 63 | * @return new hasher using the provided key 64 | */ 65 | @Override 66 | public @NotNull Hash newKeyedHash(byte @NotNull [] key) { 67 | if (key.length != 32) { 68 | throw new InvalidKeyException("Keys must be 32 bytes"); 69 | } 70 | return new Blake3Hash(ByteOps.unpackIntsLE(key, 0, 8), Constants.KEYED_HASH); 71 | } 72 | 73 | /** 74 | * Creates a fresh BLAKE3 hasher in key derivation mode using the provided context data. 75 | * 76 | * @param context initial data to derive keys from such as a master key or some unique identifier 77 | * @return new hasher for performing key derivation 78 | */ 79 | @Override 80 | public @NotNull Hash newKeyDerivationFunction(byte @NotNull [] context) { 81 | Blake3Hash ctxHasher = new Blake3Hash(Constants.IV, Constants.DERIVE_KEY_CONTEXT); 82 | ctxHasher.inputData(context, 0, context.length); 83 | byte[] key = new byte[Constants.KEY_LEN]; 84 | ctxHasher.outputHash(key, 0, Constants.KEY_LEN); 85 | return new Blake3Hash(ByteOps.unpackIntsLE(key, 0, 8), Constants.DERIVE_KEY_MATERIAL); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/impl/blake3/Blake3RandomBytesGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.impl.blake3; 22 | 23 | import dev.o1c.spi.Hash; 24 | import dev.o1c.spi.RandomBytesGenerator; 25 | import dev.o1c.spi.SeedGenerator; 26 | import org.jetbrains.annotations.NotNull; 27 | 28 | // https://github.com/BLAKE3-team/BLAKE3-specs/blob/master/blake3.pdf 29 | // https://doi.org/10.6028/NIST.SP.800-90Ar1 30 | public class Blake3RandomBytesGenerator implements RandomBytesGenerator { 31 | private static final ThreadLocal CURRENT = new ThreadLocal<>(); 32 | private static final long RESEED_INTERVAL = 1L << 48; 33 | private Hash hash; 34 | private long counter; 35 | 36 | public Blake3RandomBytesGenerator() { 37 | reseed(); 38 | } 39 | 40 | private void reseed() { 41 | counter = 0; 42 | byte[] seed = SeedGenerator.getInstance().generateSeed(32); 43 | hash = Blake3HashFactory.INSTANCE.newKeyedHash(seed); 44 | } 45 | 46 | private void ratchet() { 47 | if (++counter == RESEED_INTERVAL) { 48 | reseed(); 49 | } else { 50 | byte[] nextKey = new byte[32]; 51 | hash.doFinalize(nextKey); 52 | hash = Blake3HashFactory.INSTANCE.newKeyedHash(nextKey); 53 | } 54 | } 55 | 56 | @Override 57 | public void generateBytes(byte @NotNull [] out, int offset, int length) { 58 | // skip over ratchet key 59 | byte[] skip = new byte[64]; 60 | hash.doFinalize(skip); 61 | hash.doFinalize(out, offset, length); 62 | ratchet(); 63 | } 64 | 65 | public static @NotNull Blake3RandomBytesGenerator getInstance() { 66 | if (CURRENT.get() == null) { 67 | CURRENT.set(new Blake3RandomBytesGenerator()); 68 | } 69 | return CURRENT.get(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/impl/blake3/ChunkState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.impl.blake3; 22 | 23 | import dev.o1c.util.ByteOps; 24 | import org.jetbrains.annotations.NotNull; 25 | import org.jetbrains.annotations.Range; 26 | 27 | import java.util.Arrays; 28 | 29 | class ChunkState { 30 | private int @NotNull [] chainingValue; 31 | private final long chunkCounter; 32 | private final @Flag int flags; 33 | 34 | private final byte[] block = new byte[Constants.BLOCK_LEN]; 35 | private @Range(from = 0, to = Constants.BLOCK_LEN) int blockLength; 36 | private int blocksCompressed; 37 | 38 | ChunkState(int @NotNull [] key, long chunkCounter, @Flag int flags) { 39 | this.chainingValue = key; 40 | this.chunkCounter = chunkCounter; 41 | this.flags = flags; 42 | } 43 | 44 | int length() { 45 | return Constants.BLOCK_LEN * blocksCompressed + blockLength; 46 | } 47 | 48 | @Flag 49 | int startFlag() { 50 | return blocksCompressed == 0 ? Constants.CHUNK_START : 0; 51 | } 52 | 53 | long chunkCounter() { 54 | return chunkCounter; 55 | } 56 | 57 | void update(byte @NotNull [] input, int offset, int length) { 58 | while (length > 0) { 59 | if (blockLength == Constants.BLOCK_LEN) { 60 | // If the block buffer is full, compress it and clear it. More 61 | // input is coming, so this compression is not CHUNK_END. 62 | int[] blockWords = ByteOps.unpackIntsLE(block, 0, 16); 63 | chainingValue = Arrays.copyOf(Blake3Hash.compress( 64 | chainingValue, blockWords, Constants.BLOCK_LEN, chunkCounter, flags | startFlag()), 8); 65 | blocksCompressed++; 66 | blockLength = 0; 67 | ByteOps.overwriteWithZeroes(block); 68 | } 69 | 70 | int want = Constants.BLOCK_LEN - blockLength; 71 | int take = Math.min(want, length); 72 | System.arraycopy(input, offset, block, blockLength, take); 73 | blockLength += take; 74 | offset += take; 75 | length -= take; 76 | } 77 | } 78 | 79 | Output output() { 80 | int[] blockWords = ByteOps.unpackIntsLE(block, 0, 16); 81 | int flags = this.flags | startFlag() | Constants.CHUNK_END; 82 | return new Output(chainingValue, blockWords, chunkCounter, blockLength, flags); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/impl/blake3/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.impl.blake3; 22 | 23 | final class Constants { 24 | static final int CHUNK_START = 1; 25 | static final int CHUNK_END = 1 << 1; 26 | static final int PARENT = 1 << 2; 27 | static final int ROOT = 1 << 3; 28 | static final int KEYED_HASH = 1 << 4; 29 | static final int DERIVE_KEY_CONTEXT = 1 << 5; 30 | static final int DERIVE_KEY_MATERIAL = 1 << 6; 31 | 32 | static final int BLOCK_LEN = 64; 33 | static final int KEY_LEN = 32; 34 | static final int OUT_LEN = 32; 35 | static final int CHUNK_LEN = 1024; 36 | static final int[] IV = 37 | { 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 }; 38 | // pre-permuted for all 7 rounds; the second row (2,6,3,...) indicates the base permutation 39 | static final byte[][] MSG_SCHEDULE = { 40 | { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, 41 | { 2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8 }, 42 | { 3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1 }, 43 | { 10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6 }, 44 | { 12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4 }, 45 | { 9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7 }, 46 | { 11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13 } 47 | }; 48 | 49 | private Constants() { 50 | throw new UnsupportedOperationException(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/impl/blake3/Flag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.impl.blake3; 22 | 23 | import org.intellij.lang.annotations.MagicConstant; 24 | 25 | import java.lang.annotation.ElementType; 26 | import java.lang.annotation.Retention; 27 | import java.lang.annotation.RetentionPolicy; 28 | import java.lang.annotation.Target; 29 | 30 | @Retention(RetentionPolicy.SOURCE) 31 | @Target({ ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD }) 32 | @MagicConstant(flags = { 33 | Constants.CHUNK_START, Constants.CHUNK_END, Constants.PARENT, Constants.ROOT, Constants.KEYED_HASH, 34 | Constants.DERIVE_KEY_CONTEXT, Constants.DERIVE_KEY_MATERIAL }) 35 | @interface Flag { 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/impl/blake3/Output.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.impl.blake3; 22 | 23 | import dev.o1c.util.ByteOps; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | import java.util.Arrays; 27 | 28 | /** 29 | * Represents the state just prior to either producing an eight word chaining value or any number of output bytes when the 30 | * {@link Constants#ROOT} flag is set. 31 | */ 32 | class Output { 33 | private final int[] inputChainingValue; 34 | private final int[] blockWords; 35 | private final long counter; 36 | private final int blockLength; 37 | private final @Flag int flags; 38 | 39 | Output(int @NotNull [] inputCV, int @NotNull [] blockWords, long counter, int blockLength, @Flag int flags) { 40 | this.inputChainingValue = inputCV; 41 | this.blockWords = blockWords; 42 | this.counter = counter; 43 | this.blockLength = blockLength; 44 | this.flags = flags; 45 | } 46 | 47 | int @NotNull [] chainingValue() { 48 | return Arrays.copyOf(Blake3Hash.compress( 49 | inputChainingValue, blockWords, blockLength, counter, flags), 8); 50 | } 51 | 52 | void rootOutputBytes(byte @NotNull [] out, int offset, int length) { 53 | int outputBlockCounter = 0; 54 | while (length > 0) { 55 | int chunkLength = Math.min(Constants.OUT_LEN * 2, length); 56 | length -= chunkLength; 57 | int[] words = Blake3Hash.compress( 58 | inputChainingValue, blockWords, blockLength, outputBlockCounter++, flags | Constants.ROOT); 59 | int wordCounter = 0; 60 | while (chunkLength > 0) { 61 | int wordLength = Math.min(Integer.BYTES, chunkLength); 62 | ByteOps.packIntLE(words[wordCounter++], out, offset, wordLength); 63 | offset += wordLength; 64 | chunkLength -= wordLength; 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/impl/chacha20/ChaCha20.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.impl.chacha20; 22 | 23 | import dev.o1c.util.ByteOps; 24 | 25 | import java.nio.charset.StandardCharsets; 26 | import java.util.Arrays; 27 | 28 | class ChaCha20 { 29 | private static final int BLOCK_BYTES = 64; 30 | private static final int BLOCK_INTS = BLOCK_BYTES / Integer.BYTES; 31 | private static final int KEY_OFFSET = 4; 32 | private static final int KEY_BYTES = 32; 33 | private static final int KEY_INTS = KEY_BYTES / Integer.BYTES; 34 | private static final int COUNTER_OFFSET = 12; 35 | private static final int HNONCE_OFFSET = 12; 36 | private static final int HNONCE_BYTES = 16; 37 | private static final int HNONCE_INTS = HNONCE_BYTES / Integer.BYTES; 38 | private static final int NONCE_OFFSET = 13; 39 | private static final int NONCE_BYTES = 12; 40 | private static final int NONCE_INTS = NONCE_BYTES / Integer.BYTES; 41 | private static final int[] ENGINE_STATE_HEADER = 42 | ByteOps.unpackIntsLE("expand 32-byte k".getBytes(StandardCharsets.US_ASCII), 0, 4); 43 | 44 | private final int[] x = new int[BLOCK_INTS]; 45 | private final int[] engineState = new int[BLOCK_INTS]; 46 | 47 | ChaCha20() { 48 | System.arraycopy(ENGINE_STATE_HEADER, 0, engineState, 0, 4); 49 | } 50 | 51 | void initKey(byte[] key) { 52 | ByteOps.unpackIntsLE(key, 0, KEY_INTS, engineState, KEY_OFFSET); 53 | } 54 | 55 | void initNonce(byte[] nonce) { 56 | ByteOps.unpackIntsLE(nonce, 0, NONCE_INTS, engineState, NONCE_OFFSET); 57 | } 58 | 59 | void initCounter(int counter) { 60 | engineState[COUNTER_OFFSET] = counter; 61 | } 62 | 63 | // one-shot usage 64 | void crypt(byte[] in, int offset, int length, byte[] out, int outOffset) { 65 | while (length > 0) { 66 | System.arraycopy(engineState, 0, x, 0, BLOCK_INTS); 67 | permute(x); 68 | int want = Math.min(BLOCK_BYTES, length); 69 | for (int i = 0, j = 0; i < want; i += Integer.BYTES, j++) { 70 | int keyStream = engineState[j] + x[j]; 71 | int take = Math.min(Integer.BYTES, length); 72 | int input = ByteOps.unpackIntLE(in, offset, take); 73 | int output = keyStream ^ input; 74 | ByteOps.packIntLE(output, out, outOffset, take); 75 | offset += take; 76 | outOffset += take; 77 | length -= take; 78 | } 79 | engineState[COUNTER_OFFSET]++; 80 | } 81 | } 82 | 83 | byte[] polyKey() { 84 | byte[] block = new byte[BLOCK_BYTES]; 85 | initCounter(0); 86 | crypt(block, 0, block.length, block, 0); 87 | return Arrays.copyOf(block, KEY_BYTES); 88 | } 89 | 90 | byte[] hKey(byte[] nonce) { 91 | ByteOps.unpackIntsLE(nonce, 0, HNONCE_INTS, engineState, HNONCE_OFFSET); 92 | System.arraycopy(engineState, 0, x, 0, BLOCK_INTS); 93 | permute(x); 94 | byte[] hKey = new byte[KEY_BYTES]; 95 | ByteOps.packIntsLE(x, 0, 4, hKey, 0); 96 | ByteOps.packIntsLE(x, 12, 4, hKey, 16); 97 | return hKey; 98 | } 99 | 100 | /** 101 | * Performs an in-place ChaCha20 permutation on the provided state array. 102 | * 103 | * @param state length 16 array of internal state decoded from a little endian byte array 104 | */ 105 | private void permute(int[] state) { 106 | for (int i = 0; i < 10; i++) { 107 | columnRound(state); 108 | diagonalRound(state); 109 | } 110 | } 111 | 112 | private static void columnRound(int[] state) { 113 | quarterRound(state, 0, 4, 8, 12); 114 | quarterRound(state, 1, 5, 9, 13); 115 | quarterRound(state, 2, 6, 10, 14); 116 | quarterRound(state, 3, 7, 11, 15); 117 | } 118 | 119 | private static void diagonalRound(int[] state) { 120 | quarterRound(state, 0, 5, 10, 15); 121 | quarterRound(state, 1, 6, 11, 12); 122 | quarterRound(state, 2, 7, 8, 13); 123 | quarterRound(state, 3, 4, 9, 14); 124 | } 125 | 126 | private static void quarterRound(int[] state, int a, int b, int c, int d) { 127 | state[a] += state[b]; 128 | state[d] = Integer.rotateLeft(state[d] ^ state[a], 16); 129 | 130 | state[c] += state[d]; 131 | state[b] = Integer.rotateLeft(state[b] ^ state[c], 12); 132 | 133 | state[a] += state[b]; 134 | state[d] = Integer.rotateLeft(state[d] ^ state[a], 8); 135 | 136 | state[c] += state[d]; 137 | state[b] = Integer.rotateLeft(state[b] ^ state[c], 7); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/impl/chacha20/ChaCha20Poly1305Cipher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.impl.chacha20; 22 | 23 | import dev.o1c.spi.Cipher; 24 | import dev.o1c.spi.InvalidAuthenticationTagException; 25 | import dev.o1c.util.Validator; 26 | import org.jetbrains.annotations.NotNull; 27 | 28 | import java.security.MessageDigest; 29 | import java.util.Arrays; 30 | 31 | /** 32 | * Implements RFC 8439 version of ChaCha20-Poly1305. 33 | * 34 | * @see RFC 8439 35 | */ 36 | public class ChaCha20Poly1305Cipher implements Cipher { 37 | private final ChaCha20 cipher = new ChaCha20(); 38 | private final Poly1305 authenticator = new Poly1305(); 39 | private long contextLength = -1; 40 | 41 | @Override 42 | public int keyLength() { 43 | return 32; 44 | } 45 | 46 | @Override 47 | public int nonceLength() { 48 | return 12; 49 | } 50 | 51 | @Override 52 | public void init(byte @NotNull [] key, byte @NotNull [] nonce, byte @NotNull [] context) { 53 | checkKeyLength(key.length); 54 | checkNonceLength(nonce.length); 55 | cipher.initKey(key); 56 | cipher.initNonce(nonce); 57 | cipher.initCounter(0); 58 | authenticator.init(cipher.polyKey()); 59 | authenticator.updatePad(context, 0, context.length); 60 | contextLength = context.length; 61 | } 62 | 63 | @Override 64 | public int tagLength() { 65 | return 16; 66 | } 67 | 68 | @Override 69 | public void encrypt( 70 | byte @NotNull [] plaintext, int ptOffset, int ptLength, byte @NotNull [] ciphertext, int ctOffset, 71 | byte @NotNull [] tag, int tagOffset) { 72 | Validator.checkBufferArgs(plaintext, ptOffset, ptLength); 73 | Validator.checkBufferArgs(ciphertext, ctOffset, ptLength); 74 | Validator.checkBufferArgs(tag, tagOffset, tagLength()); 75 | if (contextLength < 0) { 76 | throw new IllegalStateException("Cipher must be initialized"); 77 | } 78 | cipher.crypt(plaintext, ptOffset, ptLength, ciphertext, ctOffset); 79 | authenticator.updatePad(ciphertext, ctOffset, ptLength); 80 | authenticator.updateLengths(contextLength, ptLength); 81 | authenticator.computeMac(tag, tagOffset); 82 | contextLength = -1; 83 | } 84 | 85 | @Override 86 | public void decrypt( 87 | byte @NotNull [] ciphertext, int ctOffset, int ctLength, byte @NotNull [] tag, int tagOffset, 88 | byte @NotNull [] plaintext, int ptOffset) { 89 | Validator.checkBufferArgs(ciphertext, ctOffset, ctLength); 90 | Validator.checkBufferArgs(tag, tagOffset, tagLength()); 91 | Validator.checkBufferArgs(plaintext, ptOffset, ctLength); 92 | if (contextLength < 0) { 93 | throw new IllegalStateException("Cipher must be initialized"); 94 | } 95 | authenticator.updatePad(ciphertext, ctOffset, ctLength); 96 | authenticator.updateLengths(contextLength, ctLength); 97 | byte[] actualTag = authenticator.computeMac(); 98 | byte[] expectedTag = Arrays.copyOfRange(tag, tagOffset, tagOffset + tagLength()); 99 | if (!MessageDigest.isEqual(expectedTag, actualTag)) { 100 | throw new InvalidAuthenticationTagException("Tag mismatch"); 101 | } 102 | cipher.crypt(ciphertext, ctOffset, ctLength, plaintext, ptOffset); 103 | contextLength = -1; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/impl/chacha20/XChaCha20Poly1305Cipher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.impl.chacha20; 22 | 23 | import dev.o1c.spi.Cipher; 24 | import dev.o1c.util.ByteOps; 25 | import org.jetbrains.annotations.NotNull; 26 | 27 | import java.util.Arrays; 28 | 29 | /** 30 | * Implements the extended-nonce cipher XChaCha20-Poly1305. 31 | * 32 | * @see RFC 8439 (ChaCha20-Poly1305) 33 | * @see Draft XChaCha20-Poly1305 34 | */ 35 | public class XChaCha20Poly1305Cipher implements Cipher { 36 | private final ChaCha20 hChaCha = new ChaCha20(); 37 | private final Cipher innerCipher = new ChaCha20Poly1305Cipher(); 38 | 39 | @Override 40 | public int keyLength() { 41 | return 32; 42 | } 43 | 44 | @Override 45 | public int nonceLength() { 46 | return 24; 47 | } 48 | 49 | @Override 50 | public void init(byte @NotNull [] key, byte @NotNull [] nonce, byte @NotNull [] context) { 51 | checkKeyLength(key.length); 52 | checkNonceLength(nonce.length); 53 | hChaCha.initKey(key); 54 | byte[] hNonce = Arrays.copyOf(nonce, 16); 55 | byte[] sNonce = Arrays.copyOfRange(nonce, 12, 24); 56 | ByteOps.overwriteWithZeroes(sNonce, 0, 4); 57 | innerCipher.init(hChaCha.hKey(hNonce), sNonce, context); 58 | } 59 | 60 | @Override 61 | public int tagLength() { 62 | return 16; 63 | } 64 | 65 | @Override 66 | public void encrypt( 67 | byte @NotNull [] plaintext, int ptOffset, int ptLength, byte @NotNull [] ciphertext, int ctOffset, 68 | byte @NotNull [] tag, int tagOffset) { 69 | innerCipher.encrypt(plaintext, ptOffset, ptLength, ciphertext, ctOffset, tag, tagOffset); 70 | } 71 | 72 | @Override 73 | public void decrypt( 74 | byte @NotNull [] ciphertext, int ctOffset, int ctLength, byte @NotNull [] tag, int tagOffset, 75 | byte @NotNull [] plaintext, int ptOffset) { 76 | innerCipher.decrypt(ciphertext, ctOffset, ctLength, tag, tagOffset, plaintext, ptOffset); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/internal/SystemRandomBytesGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.internal; 22 | 23 | import dev.o1c.spi.InvalidProviderException; 24 | import dev.o1c.spi.RandomBytesGenerator; 25 | import dev.o1c.util.Validator; 26 | import org.jetbrains.annotations.NotNull; 27 | 28 | import java.security.NoSuchAlgorithmException; 29 | import java.security.SecureRandom; 30 | 31 | public class SystemRandomBytesGenerator implements RandomBytesGenerator { 32 | private static final ThreadLocal CURRENT = new ThreadLocal<>(); 33 | 34 | private final SecureRandom random; 35 | 36 | public SystemRandomBytesGenerator() { 37 | try { 38 | random = SecureRandom.getInstanceStrong(); 39 | } catch (NoSuchAlgorithmException e) { 40 | throw new InvalidProviderException(e); 41 | } 42 | } 43 | 44 | @Override 45 | public void generateBytes(byte @NotNull [] out) { 46 | random.nextBytes(out); 47 | } 48 | 49 | @Override 50 | public void generateBytes(byte @NotNull [] out, int offset, int length) { 51 | Validator.checkBufferArgs(out, offset, length); 52 | byte[] bytes = new byte[length]; 53 | random.nextBytes(bytes); 54 | System.arraycopy(bytes, 0, out, offset, length); 55 | } 56 | 57 | public static @NotNull SystemRandomBytesGenerator getInstance() { 58 | if (CURRENT.get() == null) { 59 | CURRENT.set(new SystemRandomBytesGenerator()); 60 | } 61 | return CURRENT.get(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/internal/SystemSeedGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.internal; 22 | 23 | import dev.o1c.spi.SeedGenerator; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | import java.security.NoSuchAlgorithmException; 27 | import java.security.SecureRandom; 28 | 29 | public class SystemSeedGenerator implements SeedGenerator { 30 | private final SecureRandom secureRandom; 31 | 32 | public SystemSeedGenerator() { 33 | SecureRandom random; 34 | try { 35 | random = SecureRandom.getInstance("NativePRNG"); 36 | } catch (NoSuchAlgorithmException noNativePRNG) { 37 | try { 38 | random = SecureRandom.getInstance("Windows-PRNG"); 39 | } catch (NoSuchAlgorithmException noWindowsPRNG) { 40 | try { 41 | random = SecureRandom.getInstanceStrong(); 42 | } catch (NoSuchAlgorithmException e) { 43 | random = new SecureRandom(); 44 | } 45 | } 46 | } 47 | secureRandom = random; 48 | } 49 | 50 | @Override 51 | public byte @NotNull [] generateSeed(int nrBytes) { 52 | return secureRandom.generateSeed(nrBytes); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/lib/O1CLib.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.lib; 22 | 23 | class O1CLib { 24 | static native void randomBytes(byte[] buf); 25 | 26 | static native void entropyBytes(byte[] buf); 27 | 28 | static native int hashStateSize(); // dependent on runtime arch 29 | 30 | static native void hashInit(byte[] hashState); 31 | 32 | static native void keyedHashInit(byte[] hashState, byte[] key); 33 | 34 | static native void kdfHashInit(byte[] hashState, byte[] context); 35 | 36 | static native void hashUpdate(byte[] hashState, byte[] in, int offset, int length); 37 | 38 | static native void hashFinal(byte[] hashState, byte[] out, int offset, int length); 39 | 40 | // optimized form allowing for stack-allocated hash state 41 | static native void hash(byte[] in, int offset, int length, byte[] hash, int hashOffset, int hashLength); 42 | 43 | static native void keyedHash(byte[] key, byte[] in, int offset, int length, byte[] out, int outOffset, int outLength); 44 | 45 | static native void scalarFieldBaseMultiply(byte[] result, byte[] scalar); 46 | 47 | static native void scalarFieldMultiply(byte[] result, byte[] scalar, byte[] fieldElement); 48 | 49 | static native void generateScalarFieldKeyPair(byte[] publicKey, byte[] privateKey); 50 | 51 | static native void authenticatedEncrypt( 52 | byte[] key, byte[] nonce, byte[] context, byte[] pt, int offset, int length, byte[] ct, int ctOffset, byte[] tag, 53 | int tagOffset); 54 | 55 | static native boolean authenticatedDecrypt( 56 | byte[] key, byte[] nonce, byte[] context, byte[] ct, int offset, int length, byte[] tag, int tagOffset, byte[] pt, 57 | int ptOffset); 58 | 59 | static native void deriveKeyPairFromSeed(byte[] publicKey, byte[] expandedPrivateKey, byte[] seed); 60 | 61 | static native void generateSignKeyPair(byte[] publicKey, byte[] expandedPrivateKey); 62 | 63 | static native void sign(byte[] expandedPrivateKey, byte[] in, int offset, int length, byte[] sig, int sigOffset); 64 | 65 | static native boolean verify(byte[] publicKey, byte[] in, int offset, int length, byte[] sig, int sigOffset); 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/lwc/ascon/Ascon.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.lwc.ascon; 22 | 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | /** 26 | * Encapsulates the Ascon128 permutations. Ported from the reference C implementation. 27 | * 28 | * @see Ascon v1.2 29 | */ 30 | class Ascon { 31 | private static final int MAX_ROUNDS = 12; 32 | 33 | private static void ascon(int rounds, long @NotNull [] s) { 34 | for (int round = MAX_ROUNDS - rounds; round < MAX_ROUNDS; round++) { 35 | int roundConstant = (15 - round << 4) | round; 36 | s[2] ^= roundConstant; 37 | // substitution layer 38 | s[0] ^= s[4]; 39 | s[4] ^= s[3]; 40 | s[2] ^= s[1]; 41 | // start of keccak s-box 42 | long t0 = ~s[0], t1 = ~s[1], t2 = ~s[2], t3 = ~s[3], t4 = ~s[4]; 43 | t0 &= s[1]; 44 | t1 &= s[2]; 45 | t2 &= s[3]; 46 | t3 &= s[4]; 47 | t4 &= s[0]; 48 | s[0] ^= t1; 49 | s[1] ^= t2; 50 | s[2] ^= t3; 51 | s[3] ^= t4; 52 | s[4] ^= t0; 53 | // end of keccak s-box 54 | s[1] ^= s[0]; 55 | s[0] ^= s[4]; 56 | s[3] ^= s[2]; 57 | s[2] = ~s[2]; 58 | // linear diffusion layer 59 | s[0] ^= Long.rotateRight(s[0], 19) ^ Long.rotateRight(s[0], 28); 60 | s[1] ^= Long.rotateRight(s[1], 61) ^ Long.rotateRight(s[1], 39); 61 | s[2] ^= Long.rotateRight(s[2], 1) ^ Long.rotateRight(s[2], 6); 62 | s[3] ^= Long.rotateRight(s[3], 10) ^ Long.rotateRight(s[3], 17); 63 | s[4] ^= Long.rotateRight(s[4], 7) ^ Long.rotateRight(s[4], 41); 64 | } 65 | } 66 | 67 | static void ascon12(long @NotNull [] s) { 68 | ascon(12, s); 69 | } 70 | 71 | static void ascon6(long @NotNull [] s) { 72 | ascon(6, s); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/lwc/ascon/AsconHash.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.lwc.ascon; 22 | 23 | import dev.o1c.spi.Hash; 24 | import dev.o1c.util.ByteOps; 25 | import org.jetbrains.annotations.NotNull; 26 | 27 | import java.nio.ByteBuffer; 28 | import java.util.Arrays; 29 | 30 | public class AsconHash implements Hash { 31 | private static final int RATE = 8; 32 | private static final byte PAD = (byte) 0x80; 33 | // 0 || rate in bits (1 byte) || # rounds (1 byte) || 0 || || hash size (32-bit) 34 | private static final long HASH_IV = 64L << 48 | 12L << 40 | 256; 35 | private static final long XOF_IV = 64L << 48 | 12L << 40; 36 | 37 | private final long[] state = new long[5]; 38 | private final ByteBuffer buffer = ByteBuffer.allocate(RATE); 39 | private final int hashSize; 40 | private final long iv; 41 | 42 | public AsconHash() { 43 | hashSize = 0; 44 | iv = XOF_IV; 45 | } 46 | 47 | public AsconHash(int hashSize) { 48 | this.hashSize = hashSize; 49 | iv = HASH_IV; 50 | } 51 | 52 | @Override 53 | public int hashLength() { 54 | return hashSize == 0 ? 32 : hashSize; 55 | } 56 | 57 | @Override 58 | public void reset() { 59 | state[0] = iv; 60 | Arrays.fill(state, 1, 5, 0); 61 | Ascon.ascon12(state); 62 | buffer.clear(); 63 | } 64 | 65 | @Override 66 | public void update(byte b) { 67 | if (!buffer.put(b).hasRemaining()) { 68 | buffer.flip(); 69 | state[0] ^= buffer.getLong(); 70 | buffer.clear(); 71 | Ascon.ascon12(state); 72 | } 73 | } 74 | 75 | @Override 76 | public void update(byte @NotNull [] in, int offset, int length) { 77 | while (length > 0) { 78 | int ps = Math.min(length, buffer.remaining()); 79 | while (ps-- > 0) { 80 | buffer.put(in[offset++]); 81 | length--; 82 | } 83 | if (!buffer.hasRemaining()) { 84 | buffer.flip(); 85 | state[0] ^= buffer.getLong(); 86 | buffer.clear(); 87 | Ascon.ascon12(state); 88 | } 89 | } 90 | } 91 | 92 | @Override 93 | public void doFinalize(byte @NotNull [] out, int offset, int length) { 94 | buffer.put(PAD); 95 | buffer.flip(); 96 | state[0] ^= ByteOps.unpackLongBE(buffer.array(), buffer.arrayOffset(), buffer.remaining()); 97 | buffer.clear(); 98 | while (length >= RATE) { 99 | Ascon.ascon12(state); 100 | ByteOps.packLongBE(state[0], out, offset, RATE); 101 | offset += RATE; 102 | length -= RATE; 103 | } 104 | if (length > 0) { 105 | Ascon.ascon12(state); 106 | ByteOps.packLongBE(state[0], out, offset, length); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/lwc/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | /** 22 | * Lightweight cryptography implementations. These algorithms are suitable for a wide class of environments ranging from tiny 23 | * embedded devices to massive servers. 24 | * 25 | * @see Lightweight Cryptography at NIST 26 | */ 27 | @ApiStatus.Experimental 28 | package dev.o1c.lwc; 29 | 30 | import org.jetbrains.annotations.ApiStatus; 31 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/lwc/xoodyak/ExtensibleOutputFunction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.lwc.xoodyak; 22 | 23 | class ExtensibleOutputFunction extends XoodyakHash { 24 | private final int hashLength; 25 | 26 | ExtensibleOutputFunction() { 27 | this(32); 28 | } 29 | 30 | ExtensibleOutputFunction(int hashLength) { 31 | this.hashLength = hashLength; 32 | } 33 | 34 | @Override 35 | public int hashLength() { 36 | return hashLength; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/lwc/xoodyak/KeyDerivationFunction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.lwc.xoodyak; 22 | 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | class KeyDerivationFunction extends XoodyakHash { 26 | private final byte[] context; 27 | 28 | KeyDerivationFunction(byte[] context) { 29 | this.context = context.clone(); 30 | } 31 | 32 | @Override 33 | public void reset() { 34 | xoodyak.initialize(); 35 | xoodyak.absorb(context, 0, context.length); 36 | } 37 | 38 | @Override 39 | public void doFinalize(byte @NotNull [] out, int offset, int length) { 40 | xoodyak.squeezeKey(out, offset, length); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/lwc/xoodyak/KeyedHash.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.lwc.xoodyak; 22 | 23 | class KeyedHash extends XoodyakHash { 24 | private final byte[] key; 25 | 26 | KeyedHash(byte[] key) { 27 | this.key = key.clone(); 28 | } 29 | 30 | @Override 31 | public void reset() { 32 | xoodyak.initialize(key); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/lwc/xoodyak/XoodyakCipher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.lwc.xoodyak; 22 | 23 | import dev.o1c.spi.Cipher; 24 | import dev.o1c.spi.InvalidAuthenticationTagException; 25 | import dev.o1c.util.Validator; 26 | import org.jetbrains.annotations.NotNull; 27 | 28 | import java.security.MessageDigest; 29 | import java.util.Arrays; 30 | 31 | class XoodyakCipher implements Cipher { 32 | private final Xoodyak xoodyak = new Xoodyak(); 33 | 34 | @Override 35 | public int keyLength() { 36 | return 16; 37 | } 38 | 39 | @Override 40 | public int nonceLength() { 41 | return 16; 42 | } 43 | 44 | @Override 45 | public void init(byte @NotNull [] key, byte @NotNull [] nonce, byte @NotNull [] context) { 46 | checkKeyLength(key.length); 47 | checkNonceLength(nonce.length); 48 | xoodyak.initialize(key); 49 | xoodyak.absorb(nonce, 0, nonce.length); 50 | xoodyak.absorb(context, 0, context.length); 51 | } 52 | 53 | @Override 54 | public int tagLength() { 55 | return 16; 56 | } 57 | 58 | @Override 59 | public void encrypt( 60 | byte @NotNull [] plaintext, int ptOffset, int ptLength, byte @NotNull [] ciphertext, int ctOffset, 61 | byte @NotNull [] tag, int tagOffset) { 62 | Validator.checkBufferArgs(plaintext, ptOffset, ptLength); 63 | Validator.checkBufferArgs(ciphertext, ctOffset, ptLength); 64 | Validator.checkBufferArgs(tag, tagOffset, tagLength()); 65 | xoodyak.encrypt(plaintext, ptOffset, ptLength, ciphertext, ctOffset); 66 | xoodyak.squeeze(tag, tagOffset, tagLength()); 67 | xoodyak.ratchet(); 68 | } 69 | 70 | @Override 71 | public void decrypt( 72 | byte @NotNull [] ciphertext, int ctOffset, int ctLength, byte @NotNull [] tag, int tagOffset, 73 | byte @NotNull [] plaintext, int ptOffset) { 74 | Validator.checkBufferArgs(ciphertext, ctOffset, ctLength); 75 | Validator.checkBufferArgs(tag, tagOffset, tagLength()); 76 | Validator.checkBufferArgs(plaintext, ptOffset, ctLength); 77 | xoodyak.decrypt(ciphertext, ctOffset, ctLength, plaintext, ptOffset); 78 | byte[] expected = Arrays.copyOfRange(tag, tagOffset, tagOffset + tagLength()); 79 | byte[] actual = new byte[tagLength()]; 80 | xoodyak.squeeze(actual, 0, actual.length); 81 | xoodyak.ratchet(); 82 | if (!MessageDigest.isEqual(expected, actual)) { 83 | throw new InvalidAuthenticationTagException("Tag mismatch"); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/lwc/xoodyak/XoodyakHash.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.lwc.xoodyak; 22 | 23 | import dev.o1c.spi.Hash; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | class XoodyakHash implements Hash { 27 | final Xoodyak xoodyak = new Xoodyak(); 28 | 29 | @Override 30 | public int hashLength() { 31 | return 32; 32 | } 33 | 34 | @Override 35 | public void reset() { 36 | xoodyak.initialize(); 37 | } 38 | 39 | @Override 40 | public void update(byte b) { 41 | xoodyak.absorb(new byte[] { b }, 0, 1); 42 | } 43 | 44 | @Override 45 | public void update(byte @NotNull [] in, int offset, int length) { 46 | xoodyak.absorb(in, offset, length); 47 | } 48 | 49 | @Override 50 | public void doFinalize(byte @NotNull [] out, int offset, int length) { 51 | xoodyak.squeeze(out, offset, length); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/lwc/xoodyak/XoodyakHashFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.lwc.xoodyak; 22 | 23 | import dev.o1c.spi.Hash; 24 | import dev.o1c.spi.HashFactory; 25 | import org.jetbrains.annotations.NotNull; 26 | import org.jetbrains.annotations.Range; 27 | 28 | public class XoodyakHashFactory implements HashFactory { 29 | @Override 30 | public @NotNull Hash newHash() { 31 | return new ExtensibleOutputFunction(); 32 | } 33 | 34 | @Override 35 | public @NotNull Hash newHash(@Range(from = 0, to = Integer.MAX_VALUE) int hashLength) { 36 | return new ExtensibleOutputFunction(hashLength); 37 | } 38 | 39 | @Override 40 | public @NotNull Hash newKeyedHash(byte @NotNull [] key) { 41 | return new KeyedHash(key); 42 | } 43 | 44 | @Override 45 | public @NotNull Hash newKeyDerivationFunction(byte @NotNull [] context) { 46 | return new KeyDerivationFunction(context); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/lwc/xoodyak/XoodyakRandomBytesGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.lwc.xoodyak; 22 | 23 | import dev.o1c.spi.RandomBytesGenerator; 24 | import dev.o1c.spi.SeedGenerator; 25 | import dev.o1c.util.ByteOps; 26 | import org.jetbrains.annotations.NotNull; 27 | 28 | // https://doi.org/10.6028/NIST.SP.800-90Ar1 29 | // implements an HMAC_DRBG using Xoodyak 30 | public class XoodyakRandomBytesGenerator implements RandomBytesGenerator { 31 | private static final ThreadLocal CURRENT = new ThreadLocal<>(); 32 | private static final int SEED_LENGTH = 42; 33 | private final Xoodyak xoodyak = new Xoodyak(); 34 | private long counter; 35 | private final byte[] counterBuf = new byte[Long.BYTES]; 36 | 37 | public XoodyakRandomBytesGenerator() { 38 | reseed(); 39 | } 40 | 41 | private void reseed() { 42 | byte[] seed = SeedGenerator.getInstance().generateSeed(SEED_LENGTH); 43 | xoodyak.initialize(seed); 44 | xoodyak.ratchet(); 45 | counter = 0; 46 | } 47 | 48 | private void ratchet() { 49 | if (++counter == 0) { 50 | reseed(); 51 | } else { 52 | // trickle in the counter similar to initialize() 53 | ByteOps.packLongBE(counter, counterBuf, 0); 54 | int offset = 0; 55 | for (int i = 0; i < Long.BYTES; i++) { 56 | if (counterBuf[i] == 0) { 57 | offset++; 58 | } 59 | } 60 | xoodyak.absorbAny(Cyclist.DomainConstant.Block, 1, counterBuf, offset, Long.BYTES - offset); 61 | xoodyak.ratchet(); 62 | } 63 | } 64 | 65 | @Override 66 | public void generateBytes(byte @NotNull [] out, int offset, int length) { 67 | xoodyak.squeeze(out, offset, length); 68 | ratchet(); 69 | } 70 | 71 | public static @NotNull XoodyakRandomBytesGenerator getInstance() { 72 | if (CURRENT.get() == null) { 73 | CURRENT.set(new XoodyakRandomBytesGenerator()); 74 | } 75 | return CURRENT.get(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/spi/Hash.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.spi; 22 | 23 | import dev.o1c.util.ByteOps; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | /** 27 | * Provides cryptographic hashes of a sequence of bytes. Hashes are one-way functions that take an essentially 28 | * arbitrary number of input bytes and calculate finite output bytes in such a way that any changes in the input bytes 29 | * will cause fairly large differences in the output bytes. 30 | */ 31 | public interface Hash { 32 | 33 | /** 34 | * Returns the default hash length output by this instance. 35 | */ 36 | int hashLength(); 37 | 38 | /** 39 | * Resets the state of this instance to begin calculating a fresh hash. 40 | */ 41 | void reset(); 42 | 43 | /** 44 | * Updates the state of this hash with the provided byte. 45 | */ 46 | void update(byte b); 47 | 48 | /** 49 | * Updates the state of this hash with the provided input byte array. 50 | */ 51 | default void update(byte @NotNull [] in) { 52 | update(in, 0, in.length); 53 | } 54 | 55 | /** 56 | * Updates the state of this hash with the provided array slice. 57 | * 58 | * @param in data to hash into this state 59 | * @param offset where to begin reading data 60 | * @param length how many bytes to hash 61 | */ 62 | void update(byte @NotNull [] in, int offset, int length); 63 | 64 | /** 65 | * Updates the state of this hash using a runtime length encoded (RLE) buffer. This encodes the length of the buffer 66 | * as a little endian 32-bit integer followed by the contents of the buffer. This is provided for convenience for 67 | * creating other primitives. 68 | * 69 | * @param buffer data to hash preceded by its data length encoded as a 32-bit little endian integer 70 | */ 71 | default void updateRLE(byte @NotNull [] buffer) { 72 | byte[] length = new byte[Integer.BYTES]; 73 | ByteOps.packIntLE(buffer.length, length, 0); 74 | update(length); 75 | update(buffer); 76 | } 77 | 78 | /** 79 | * Finalizes this hash state into the provided array slice. This allows for extensible output functions when the 80 | * provided length is not the same as the {@linkplain #hashLength() default hash length}. 81 | * 82 | * @param out destination array to write hash output to 83 | * @param offset where to begin writing hash output 84 | * @param length how many bytes to output 85 | */ 86 | void doFinalize(byte @NotNull [] out, int offset, int length); 87 | 88 | /** 89 | * Finalizes this hash state into the provided array and offset with the 90 | * {@linkplain #hashLength() default hash length}. 91 | * 92 | * @param out destination array to write hash output to 93 | * @param offset where to begin writing hash output 94 | */ 95 | default void doFinalize(byte @NotNull [] out, int offset) { 96 | doFinalize(out, offset, hashLength()); 97 | } 98 | 99 | /** 100 | * Finalizes this hash state into the provided array. 101 | * 102 | * @param out destination array to write hash output to 103 | */ 104 | default void doFinalize(byte @NotNull [] out) { 105 | doFinalize(out, 0, out.length); 106 | } 107 | 108 | /** 109 | * Finalizes this hash state and returns the {@linkplain #hashLength() default hash length} bytes of hash output. 110 | */ 111 | default byte @NotNull [] doFinalize() { 112 | byte[] hash = new byte[hashLength()]; 113 | doFinalize(hash); 114 | return hash; 115 | } 116 | 117 | /** 118 | * Calculates the hash of the provided array slice in one pass. 119 | * 120 | * @param data input data to calculate hash of 121 | * @param offset where in data to begin reading data to hash 122 | * @param length how many bytes of data to read 123 | * @return {@linkplain #hashLength() default hash length} byte array output 124 | */ 125 | default byte @NotNull [] hash(byte @NotNull [] data, int offset, int length) { 126 | reset(); 127 | update(data, offset, length); 128 | byte[] hash = new byte[hashLength()]; 129 | doFinalize(hash); 130 | return hash; 131 | } 132 | 133 | /** 134 | * Calculates the hash of the provided array in one pass. 135 | * 136 | * @param data input data to calculate hash of 137 | * @return {@linkplain #hashLength() default hash length} byte array output 138 | */ 139 | default byte @NotNull [] hash(byte @NotNull [] data) { 140 | return hash(data, 0, data.length); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/spi/HashFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.spi; 22 | 23 | import org.jetbrains.annotations.NotNull; 24 | import org.jetbrains.annotations.Range; 25 | 26 | import java.nio.charset.StandardCharsets; 27 | 28 | /** 29 | * Creates cryptographic hash instance variants for various use cases. 30 | */ 31 | public interface HashFactory { 32 | 33 | /** 34 | * Creates a new plain Hash instance with an algorithm-specific default hash output length. 35 | */ 36 | @NotNull Hash newHash(); 37 | 38 | /** 39 | * Creates a new plain Hash instance with the provided default hash output length. 40 | */ 41 | @NotNull Hash newHash(@Range(from = 0, to = Integer.MAX_VALUE) int hashLength); 42 | 43 | /** 44 | * Creates a new keyed Hash instance using the provided secret key. 45 | */ 46 | @NotNull Hash newKeyedHash(byte @NotNull [] key); 47 | 48 | /** 49 | * Creates a new key derivation function (KDF) Hash instance for the provided key derivation context. 50 | */ 51 | @NotNull Hash newKeyDerivationFunction(byte @NotNull [] context); 52 | 53 | /** 54 | * Creates a new key derivation function (KDF) Hash instance for the provided key derivation context. 55 | */ 56 | default @NotNull Hash newKeyDerivationFunction(@NotNull String context) { 57 | return newKeyDerivationFunction(context.getBytes(StandardCharsets.UTF_8)); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/spi/InvalidAuthenticationTagException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.spi; 22 | 23 | import dev.o1c.O1CException; 24 | 25 | public class InvalidAuthenticationTagException extends O1CException { 26 | public InvalidAuthenticationTagException(String message) { 27 | super(message); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/spi/InvalidKeyException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.spi; 22 | 23 | import dev.o1c.O1CException; 24 | 25 | public class InvalidKeyException extends O1CException { 26 | public InvalidKeyException(String message) { 27 | super(message); 28 | } 29 | 30 | public InvalidKeyException(String message, Throwable cause) { 31 | super(message, cause); 32 | } 33 | 34 | public InvalidKeyException(Throwable cause) { 35 | super(cause); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/spi/InvalidProviderException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.spi; 22 | 23 | import dev.o1c.O1CException; 24 | 25 | public class InvalidProviderException extends O1CException { 26 | public InvalidProviderException(String message) { 27 | super(message); 28 | } 29 | 30 | public InvalidProviderException(Throwable cause) { 31 | super(cause); 32 | } 33 | 34 | public InvalidProviderException(String message, Throwable... suppressedExceptions) { 35 | super(message); 36 | for (Throwable suppressedException : suppressedExceptions) { 37 | addSuppressed(suppressedException); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/spi/InvalidSignatureException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.spi; 22 | 23 | import dev.o1c.O1CException; 24 | 25 | public class InvalidSignatureException extends O1CException { 26 | public InvalidSignatureException(String message) { 27 | super(message); 28 | } 29 | 30 | public InvalidSignatureException(Throwable cause) { 31 | super(cause); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/spi/RandomBytesGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.spi; 22 | 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | /** 26 | * Generates random data suitable for cryptographic applications such as key and nonce generation. 27 | */ 28 | public interface RandomBytesGenerator { 29 | /** 30 | * Generates the requested number of bytes. 31 | * 32 | * @param nrBytes number of bytes to generate 33 | * @return the requested number of randomly generated bytes 34 | */ 35 | default byte @NotNull [] generateBytes(int nrBytes) { 36 | byte[] bytes = new byte[nrBytes]; 37 | generateBytes(bytes); 38 | return bytes; 39 | } 40 | 41 | /** 42 | * Generates random bytes and writes them to the provided array. 43 | * 44 | * @param out destination buffer to write random bytes to 45 | */ 46 | default void generateBytes(byte @NotNull [] out) { 47 | generateBytes(out, 0, out.length); 48 | } 49 | 50 | /** 51 | * Generates random bytes and writes them to the provided slice. 52 | * 53 | * @param out array to write random bytes to 54 | * @param offset where to begin writing 55 | * @param length how many bytes to generate 56 | */ 57 | void generateBytes(byte @NotNull [] out, int offset, int length); 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/spi/SeedGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.spi; 22 | 23 | import org.jetbrains.annotations.ApiStatus; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | import java.util.ServiceLoader; 27 | 28 | /** 29 | * Generates seed data for {@link RandomBytesGenerator} implementations using system entropy sources. 30 | */ 31 | @ApiStatus.Internal 32 | public interface SeedGenerator { 33 | 34 | /** 35 | * Generates the requested number of bytes of entropy. 36 | * 37 | * @param nrBytes how many bytes of entropy to gather 38 | * @return a new cryptographic seed 39 | */ 40 | byte @NotNull [] generateSeed(int nrBytes); 41 | 42 | /** 43 | * Gets the default SeedGenerator. 44 | * 45 | * @return the default SeedGenerator 46 | */ 47 | static @NotNull SeedGenerator getInstance() { 48 | for (SeedGenerator generator : ServiceLoader.load(SeedGenerator.class)) { 49 | return generator; 50 | } 51 | throw new InvalidProviderException("No SeedGenerator providers found"); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/spi/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | /** 22 | * Service provider interfaces for cryptographic primitives used for building higher level security APIs. 23 | */ 24 | @ApiStatus.Internal 25 | package dev.o1c.spi; 26 | 27 | import org.jetbrains.annotations.ApiStatus; 28 | -------------------------------------------------------------------------------- /src/main/java/dev/o1c/util/Validator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.util; 22 | 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | public final class Validator { 26 | private Validator() { 27 | } 28 | 29 | public static void checkBufferArgs(byte @NotNull [] buffer, int offset, int length) { 30 | if (offset < 0) { 31 | throw new IndexOutOfBoundsException("Offset must be non-negative but got " + offset); 32 | } 33 | if (length < 0) { 34 | throw new IndexOutOfBoundsException("Length must be non-negative but got " + length); 35 | } 36 | if (offset > buffer.length - length) { 37 | throw new IndexOutOfBoundsException( 38 | "Offset " + offset + " and length " + length + " does not fit in provided array"); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/dev.o1c.KeyManager: -------------------------------------------------------------------------------- 1 | # 2 | # ISC License 3 | # 4 | # Copyright (c) 2021, Matt Sicker 5 | # 6 | # Permission to use, copy, modify, and/or distribute this software for any 7 | # purpose with or without fee is hereby granted, provided that the above 8 | # copyright notice and this permission notice appear in all copies. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | # 18 | # SPDX-License-Identifier: ISC 19 | # 20 | 21 | # 22 | # ISC License 23 | # 24 | # Copyright (c) 2021, Matt Sicker 25 | # 26 | # Permission to use, copy, modify, and/or distribute this software for any 27 | # purpose with or without fee is hereby granted, provided that the above 28 | # copyright notice and this permission notice appear in all copies. 29 | # 30 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 31 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 32 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 33 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 34 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 35 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 36 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 37 | # 38 | # SPDX-License-Identifier: ISC 39 | # 40 | 41 | dev.o1c.impl.DefaultKeyManager 42 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/dev.o1c.spi.SeedGenerator: -------------------------------------------------------------------------------- 1 | # 2 | # ISC License 3 | # 4 | # Copyright (c) 2021, Matt Sicker 5 | # 6 | # Permission to use, copy, modify, and/or distribute this software for any 7 | # purpose with or without fee is hereby granted, provided that the above 8 | # copyright notice and this permission notice appear in all copies. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | # 18 | # SPDX-License-Identifier: ISC 19 | # 20 | 21 | dev.o1c.internal.SystemSeedGenerator 22 | -------------------------------------------------------------------------------- /src/test/c/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(PkgConfig REQUIRED) 2 | pkg_check_modules(SODIUM IMPORTED_TARGET libsodium) 3 | if (SODIUM_FOUND) 4 | # use libsodium for validating some algorithm compatibility 5 | # this executable generates fresh test_*.h test vector files 6 | add_executable(gen gen.c) 7 | target_link_libraries(gen PkgConfig::SODIUM) 8 | endif () 9 | 10 | link_libraries(o1c m) 11 | include_directories(../src/include) 12 | include_directories(../include) 13 | 14 | add_executable(test_chacha20 test_chacha20.c) 15 | add_test(NAME ChaCha20 COMMAND test_chacha20) 16 | 17 | add_executable(test_curve25519 test_curve25519.c) 18 | add_test(NAME X25519 COMMAND test_curve25519) 19 | 20 | add_executable(test_poly1305 test_poly1305.c) 21 | add_test(NAME Poly1305 COMMAND test_poly1305) 22 | 23 | add_executable(test_chacha20poly1305 test_xchacha20poly1305.c) 24 | add_test(NAME XChaCha20Poly1305 COMMAND test_chacha20poly1305) 25 | 26 | add_executable(test_ed25519 test_ed25519.c) 27 | add_test(NAME Ed25519 COMMAND test_ed25519) 28 | 29 | add_executable(test_ristretto255 test_ristretto255.c) 30 | add_test(NAME Ristretto255 COMMAND test_ristretto255) 31 | 32 | add_executable(test_scalar25519 test_scalar25519.c) 33 | add_test(NAME Scalar25519 COMMAND test_scalar25519) 34 | 35 | add_executable(test_ristretto255b3 test_ristretto255b3.c) 36 | add_test(NAME Ristretto255B3 COMMAND test_ristretto255b3) 37 | -------------------------------------------------------------------------------- /src/test/c/test.h: -------------------------------------------------------------------------------- 1 | #ifndef O1C_TEST_H 2 | #define O1C_TEST_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | // Fills a buffer with the repeating sequence 0,1,2,...,255,0,1,... 15 | static inline void init_buf(uint8_t *b, const size_t len) { 16 | for (size_t i = 0; i < len; ++i) b[i] = (uint8_t) (i % UINT8_MAX); 17 | } 18 | 19 | static inline noreturn void 20 | die(bool print_errno, const char *format, ...) { 21 | va_list ap; 22 | va_start(ap, format); 23 | vfprintf(stderr, format, ap); 24 | va_end(ap); 25 | if (print_errno) { 26 | fprintf(stderr, "- %s", strerror(errno)); 27 | } 28 | fprintf(stderr, "\n"); 29 | exit(1); 30 | } 31 | 32 | #endif //O1C_TEST_H 33 | -------------------------------------------------------------------------------- /src/test/c/test_chacha20.c: -------------------------------------------------------------------------------- 1 | #include "chacha20.h" 2 | #include "util.h" 3 | 4 | #include "test.h" 5 | 6 | typedef struct { 7 | size_t bytes; 8 | char key[o1c_chacha20_KEY_BYTES * 2 + 1]; 9 | char nonce[o1c_chacha20_NONCE_BYTES * 2 + 1]; 10 | char *keystream; 11 | char *ciphertext; 12 | } o1c_test_vector; 13 | 14 | void run_checks(const o1c_test_vector *test) { 15 | uint8_t key[o1c_chacha20_KEY_BYTES], nonce[o1c_chacha20_NONCE_BYTES]; 16 | uint8_t keystream[test->bytes], ciphertext[test->bytes]; 17 | o1c_hex2bin(key, o1c_chacha20_KEY_BYTES, test->key, o1c_chacha20_KEY_BYTES * 2); 18 | o1c_hex2bin(nonce, o1c_chacha20_NONCE_BYTES, test->nonce, o1c_chacha20_NONCE_BYTES * 2); 19 | o1c_hex2bin(keystream, test->bytes, test->keystream, test->bytes * 2); 20 | o1c_hex2bin(ciphertext, test->bytes, test->ciphertext, test->bytes * 2); 21 | uint8_t actual[test->bytes]; 22 | o1c_chacha20_stream(actual, test->bytes, nonce, key); 23 | assert(o1c_mem_eq(keystream, actual, test->bytes)); 24 | uint8_t plaintext[test->bytes]; 25 | init_buf(plaintext, test->bytes); 26 | o1c_chacha20_xor(actual, plaintext, test->bytes, nonce, key); 27 | assert(o1c_mem_eq(ciphertext, actual, test->bytes)); 28 | } 29 | 30 | #include "test_chacha20.txt" 31 | 32 | int main() { 33 | for (size_t i = 0; i < 256; ++i) run_checks(&data[i]); 34 | } 35 | -------------------------------------------------------------------------------- /src/test/c/test_curve25519.c: -------------------------------------------------------------------------------- 1 | #include "x25519.h" 2 | #include "util.h" 3 | #include "test.h" 4 | 5 | typedef struct { 6 | char sa[o1c_x25519_SCALAR_BYTES * 2 + 1]; 7 | char sb[o1c_x25519_SCALAR_BYTES * 2 + 1]; 8 | char ea[o1c_x25519_ELEMENT_BYTES * 2 + 1]; 9 | char eb[o1c_x25519_ELEMENT_BYTES * 2 + 1]; 10 | char product[o1c_x25519_ELEMENT_BYTES * 2 + 1]; 11 | } o1c_test_vector; 12 | 13 | void run_checks(const o1c_test_vector *test) { 14 | o1c_x25519_scalar_t sa, sb; 15 | o1c_x25519_element_t ea, eb, product, result; 16 | o1c_hex2bin(sa->v, o1c_x25519_SCALAR_BYTES, test->sa, o1c_x25519_SCALAR_BYTES * 2); 17 | o1c_hex2bin(sb->v, o1c_x25519_SCALAR_BYTES, test->sb, o1c_x25519_SCALAR_BYTES * 2); 18 | o1c_hex2bin(ea->v, o1c_x25519_ELEMENT_BYTES, test->ea, o1c_x25519_ELEMENT_BYTES * 2); 19 | o1c_hex2bin(eb->v, o1c_x25519_ELEMENT_BYTES, test->eb, o1c_x25519_ELEMENT_BYTES * 2); 20 | o1c_hex2bin(product->v, o1c_x25519_ELEMENT_BYTES, test->product, o1c_x25519_ELEMENT_BYTES * 2); 21 | o1c_x25519_scalar_mul_base(result, sa); 22 | assert(o1c_mem_eq(ea->v, result->v, o1c_x25519_ELEMENT_BYTES)); 23 | o1c_x25519_scalar_mul_base(result, sb); 24 | assert(o1c_mem_eq(eb->v, result->v, o1c_x25519_ELEMENT_BYTES)); 25 | assert(o1c_x25519_scalar_mul(result, sa, eb)); 26 | assert(o1c_mem_eq(product->v, result->v, o1c_x25519_ELEMENT_BYTES)); 27 | assert(o1c_x25519_scalar_mul(result, sb, ea)); 28 | assert(o1c_mem_eq(product->v, result->v, o1c_x25519_ELEMENT_BYTES)); 29 | } 30 | 31 | void smoke_test(void) { 32 | o1c_x25519_scalar_t a, b; 33 | o1c_x25519_element_t A, B, AB, BA; 34 | o1c_x25519_keypair(A, a); 35 | o1c_x25519_keypair(B, b); 36 | assert(o1c_x25519_scalar_mul(AB, a, B)); 37 | assert(o1c_x25519_scalar_mul(BA, b, A)); 38 | assert(o1c_mem_eq(AB->v, BA->v, o1c_x25519_ELEMENT_BYTES)); 39 | } 40 | 41 | #include "test_curve25519.txt" 42 | 43 | int main(void) { 44 | smoke_test(); 45 | for (size_t i = 0; i <= 256; ++i) run_checks(&data[i]); 46 | } 47 | -------------------------------------------------------------------------------- /src/test/c/test_ed25519.c: -------------------------------------------------------------------------------- 1 | #include "ed25519.h" 2 | #include "util.h" 3 | #include "test.h" 4 | 5 | typedef struct { 6 | size_t bytes; 7 | char seed[o1c_ed25519_SEED_BYTES * 2 + 1]; 8 | char expanded_key[o1c_ed25519_EXPANDED_BYTES * 2 + 1]; 9 | char public_key[o1c_ed25519_PUBLIC_BYTES * 2 + 1]; 10 | char sig[o1c_ed25519_SIGN_BYTES * 2 + 1]; 11 | } o1c_test_vector; 12 | 13 | void run_checks(const o1c_test_vector *test) { 14 | o1c_ed25519_seed_t seed; 15 | o1c_ed25519_expanded_key_t expanded_key, actual_expanded; 16 | o1c_ed25519_public_key_t public_key; 17 | uint8_t sig[o1c_ed25519_SIGN_BYTES]; 18 | uint8_t msg[test->bytes]; 19 | init_buf(msg, sizeof msg); 20 | o1c_hex2bin(seed->v, o1c_ed25519_SEED_BYTES, test->seed, o1c_ed25519_SEED_BYTES * 2); 21 | o1c_hex2bin(expanded_key->v, o1c_ed25519_EXPANDED_BYTES, test->expanded_key, o1c_ed25519_EXPANDED_BYTES * 2); 22 | o1c_hex2bin(public_key->v, o1c_ed25519_PUBLIC_BYTES, test->public_key, o1c_ed25519_PUBLIC_BYTES * 2); 23 | o1c_hex2bin(sig, o1c_ed25519_SIGN_BYTES, test->sig, o1c_ed25519_SIGN_BYTES * 2); 24 | o1c_ed25519_expand_key(actual_expanded, seed); 25 | assert(o1c_mem_eq(expanded_key->v, actual_expanded->v, o1c_ed25519_EXPANDED_BYTES)); 26 | uint8_t actual_sig[o1c_ed25519_SIGN_BYTES]; 27 | o1c_ed25519_sign(actual_sig, msg, sizeof msg, expanded_key); 28 | assert(o1c_mem_eq(sig, actual_sig, o1c_ed25519_SIGN_BYTES)); 29 | assert(o1c_ed25519_verify(sig, msg, sizeof msg, public_key)); 30 | } 31 | 32 | #include "test_ed25519.txt" 33 | 34 | int main() { 35 | for (size_t i = 0; i <= 256; ++i) run_checks(&data[i]); 36 | } 37 | -------------------------------------------------------------------------------- /src/test/c/test_poly1305.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "poly1305.h" 4 | #include "util.h" 5 | #include "test.h" 6 | 7 | static bool poly1305_smoke_test() { 8 | /* example from nacl */ 9 | static const unsigned char nacl_key[32] = { 10 | 0xee,0xa6,0xa7,0x25,0x1c,0x1e,0x72,0x91, 11 | 0x6d,0x11,0xc2,0xcb,0x21,0x4d,0x3c,0x25, 12 | 0x25,0x39,0x12,0x1d,0x8e,0x23,0x4e,0x65, 13 | 0x2d,0x65,0x1f,0xa4,0xc8,0xcf,0xf8,0x80, 14 | }; 15 | 16 | static const unsigned char nacl_msg[131] = { 17 | 0x8e,0x99,0x3b,0x9f,0x48,0x68,0x12,0x73, 18 | 0xc2,0x96,0x50,0xba,0x32,0xfc,0x76,0xce, 19 | 0x48,0x33,0x2e,0xa7,0x16,0x4d,0x96,0xa4, 20 | 0x47,0x6f,0xb8,0xc5,0x31,0xa1,0x18,0x6a, 21 | 0xc0,0xdf,0xc1,0x7c,0x98,0xdc,0xe8,0x7b, 22 | 0x4d,0xa7,0xf0,0x11,0xec,0x48,0xc9,0x72, 23 | 0x71,0xd2,0xc2,0x0f,0x9b,0x92,0x8f,0xe2, 24 | 0x27,0x0d,0x6f,0xb8,0x63,0xd5,0x17,0x38, 25 | 0xb4,0x8e,0xee,0xe3,0x14,0xa7,0xcc,0x8a, 26 | 0xb9,0x32,0x16,0x45,0x48,0xe5,0x26,0xae, 27 | 0x90,0x22,0x43,0x68,0x51,0x7a,0xcf,0xea, 28 | 0xbd,0x6b,0xb3,0x73,0x2b,0xc0,0xe9,0xda, 29 | 0x99,0x83,0x2b,0x61,0xca,0x01,0xb6,0xde, 30 | 0x56,0x24,0x4a,0x9e,0x88,0xd5,0xf9,0xb3, 31 | 0x79,0x73,0xf6,0x22,0xa4,0x3d,0x14,0xa6, 32 | 0x59,0x9b,0x1f,0x65,0x4c,0xb4,0x5a,0x74, 33 | 0xe3,0x55,0xa5 34 | }; 35 | 36 | static const unsigned char nacl_mac[16] = { 37 | 0xf3,0xff,0xc7,0x70,0x3f,0x94,0x00,0xe5, 38 | 0x2a,0x7d,0xfb,0x4b,0x3d,0x33,0x05,0xd9 39 | }; 40 | 41 | /* generates a final value of (2^130 - 2) == 3 */ 42 | static const unsigned char wrap_key[32] = { 43 | 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 44 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 45 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 46 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 47 | }; 48 | 49 | static const unsigned char wrap_msg[16] = { 50 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 51 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff 52 | }; 53 | 54 | static const unsigned char wrap_mac[16] = { 55 | 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 56 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 57 | }; 58 | 59 | /* 60 | mac of the macs of messages of length 0 to 256, where the key and messages 61 | have all their values set to the length 62 | */ 63 | static const unsigned char total_key[32] = { 64 | 0x01,0x02,0x03,0x04,0x05,0x06,0x07, 65 | 0xff,0xfe,0xfd,0xfc,0xfb,0xfa,0xf9, 66 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff, 67 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff 68 | }; 69 | 70 | static const unsigned char total_mac[16] = { 71 | 0x64,0xaf,0xe2,0xe8,0xd6,0xad,0x7b,0xbd, 72 | 0xd2,0x87,0xf9,0x7c,0x44,0x62,0x3d,0x39 73 | }; 74 | 75 | o1c_poly1305_t ctx; 76 | o1c_poly1305_t total_ctx; 77 | unsigned char all_key[32]; 78 | unsigned char all_msg[256]; 79 | unsigned char mac[16]; 80 | size_t i, j; 81 | int result = 1; 82 | 83 | for (i = 0; i < sizeof(mac); i++) 84 | mac[i] = 0; 85 | o1c_poly1305(mac, nacl_msg, sizeof(nacl_msg), nacl_key); 86 | result &= o1c_mem_eq(nacl_mac, mac, o1c_poly1305_TAG_BYTES); 87 | 88 | for (i = 0; i < sizeof(mac); i++) 89 | mac[i] = 0; 90 | o1c_poly1305_key_setup(ctx, nacl_key); 91 | o1c_poly1305_update(ctx, nacl_msg + 0, 32); 92 | o1c_poly1305_update(ctx, nacl_msg + 32, 64); 93 | o1c_poly1305_update(ctx, nacl_msg + 96, 16); 94 | o1c_poly1305_update(ctx, nacl_msg + 112, 8); 95 | o1c_poly1305_update(ctx, nacl_msg + 120, 4); 96 | o1c_poly1305_update(ctx, nacl_msg + 124, 2); 97 | o1c_poly1305_update(ctx, nacl_msg + 126, 1); 98 | o1c_poly1305_update(ctx, nacl_msg + 127, 1); 99 | o1c_poly1305_update(ctx, nacl_msg + 128, 1); 100 | o1c_poly1305_update(ctx, nacl_msg + 129, 1); 101 | o1c_poly1305_update(ctx, nacl_msg + 130, 1); 102 | o1c_poly1305_final(ctx, mac); 103 | result &= o1c_mem_eq(nacl_mac, mac, o1c_poly1305_TAG_BYTES); 104 | 105 | for (i = 0; i < sizeof(mac); i++) 106 | mac[i] = 0; 107 | o1c_poly1305(mac, wrap_msg, sizeof(wrap_msg), wrap_key); 108 | result &= o1c_mem_eq(wrap_mac, mac, o1c_poly1305_TAG_BYTES); 109 | 110 | o1c_poly1305_key_setup(total_ctx, total_key); 111 | for (i = 0; i < 256; i++) { 112 | /* set key and message to 'i,i,i..' */ 113 | for (j = 0; j < sizeof(all_key); j++) 114 | all_key[j] = i; 115 | for (j = 0; j < i; j++) 116 | all_msg[j] = i; 117 | o1c_poly1305(mac, all_msg, i, all_key); 118 | o1c_poly1305_update(total_ctx, mac, 16); 119 | } 120 | o1c_poly1305_final(total_ctx, mac); 121 | result &= o1c_mem_eq(total_mac, mac, o1c_poly1305_TAG_BYTES); 122 | 123 | return result; 124 | } 125 | 126 | int main(void) { 127 | return poly1305_smoke_test() ? EXIT_SUCCESS : EXIT_FAILURE; 128 | } 129 | -------------------------------------------------------------------------------- /src/test/c/test_ristretto255.c: -------------------------------------------------------------------------------- 1 | #include "ristretto255.h" 2 | #include "util.h" 3 | 4 | typedef struct { 5 | char scalar[o1c_scalar25519_BYTES * 2 + 1]; 6 | char element[o1c_ristretto255_BYTES * 2 + 1]; 7 | char hash[o1c_ristretto255_HASH_BYTES * 2 + 1]; 8 | char point[o1c_ristretto255_BYTES * 2 + 1]; 9 | char product[o1c_ristretto255_BYTES * 2 + 1]; 10 | } o1c_test_vector; 11 | 12 | void hex2ret(o1c_ristretto255_t r, const char *hex) { 13 | uint8_t serialized[o1c_ristretto255_BYTES]; 14 | o1c_hex2bin(serialized, o1c_ristretto255_BYTES, hex, o1c_ristretto255_BYTES * 2); 15 | o1c_ristretto255_deserialize(r, serialized); 16 | } 17 | 18 | void hex2scalar(o1c_scalar25519_t s, const char *hex) { 19 | uint8_t serialized[o1c_scalar25519_BYTES]; 20 | o1c_hex2bin(serialized, o1c_scalar25519_BYTES, hex, o1c_scalar25519_BYTES * 2); 21 | o1c_scalar25519_deserialize(s, serialized); 22 | } 23 | 24 | void run_checks(const o1c_test_vector *test) { 25 | o1c_scalar25519_t scalar; 26 | o1c_ristretto255_t element, point, product, actual; 27 | uint8_t hash[o1c_ristretto255_HASH_BYTES]; 28 | hex2scalar(scalar, test->scalar); 29 | hex2ret(element, test->element); 30 | hex2ret(point, test->point); 31 | o1c_hex2bin(hash, o1c_ristretto255_HASH_BYTES, test->hash, o1c_ristretto255_HASH_BYTES * 2); 32 | hex2ret(product, test->product); 33 | assert(o1c_ristretto255_scalar_mul_base(actual, scalar)); 34 | assert(o1c_ristretto255_equal(element, actual)); 35 | o1c_ristretto255_from_hash(actual, hash); 36 | assert(o1c_ristretto255_equal(point, actual)); 37 | assert(o1c_ristretto255_scalar_mul(actual, scalar, point)); 38 | assert(o1c_ristretto255_equal(product, actual)); 39 | } 40 | 41 | #include "test_ristretto255.txt" 42 | 43 | int main(void) { 44 | for (size_t i = 0; i <= 256; ++i) run_checks(&data[i]); 45 | } 46 | -------------------------------------------------------------------------------- /src/test/c/test_ristretto255b3.c: -------------------------------------------------------------------------------- 1 | #include "ristretto255.h" 2 | #include "drbg.h" 3 | 4 | #include 5 | 6 | void smoke_test(void) { 7 | uint8_t sk[o1c_ristretto255_KEY_BYTES], pk[o1c_ristretto255_BYTES]; 8 | drbg_randombytes(sk, o1c_ristretto255_KEY_BYTES); 9 | o1c_ristretto255b3_derive_pubkey(pk, sk); 10 | uint8_t msg[2043]; 11 | drbg_randombytes(msg, sizeof msg); 12 | uint8_t sig[o1c_ristretto255_SIGN_BYTES]; 13 | o1c_ristretto255b3_sign(sig, msg, sizeof msg, sk); 14 | assert(o1c_ristretto255b3_verify(sig, msg, sizeof msg, pk)); 15 | } 16 | 17 | int main(void) { 18 | smoke_test(); 19 | } 20 | -------------------------------------------------------------------------------- /src/test/c/test_scalar25519.c: -------------------------------------------------------------------------------- 1 | #include "scalar25519.h" 2 | #include "util.h" 3 | #include "test.h" 4 | 5 | typedef struct { 6 | char a[o1c_scalar25519_BYTES * 2 + 1]; 7 | char b[o1c_scalar25519_BYTES * 2 + 1]; 8 | char c[o1c_scalar25519_BYTES * 2 + 1]; 9 | char neg_a[o1c_scalar25519_BYTES * 2 + 1]; 10 | char neg_b[o1c_scalar25519_BYTES * 2 + 1]; 11 | char ab_c_prod_sum[o1c_scalar25519_BYTES * 2 + 1]; 12 | char ab_c_prod_diff[o1c_scalar25519_BYTES * 2 + 1]; 13 | char nonreduced[o1c_scalar25519_NONREDUCED_BYTES * 2 + 1]; 14 | char reduced[o1c_scalar25519_BYTES * 2 + 1]; 15 | } o1c_test_vector; 16 | 17 | #define assert_eq(a,b) assert(o1c_mem_eq((a)->v,(b)->v,o1c_scalar25519_BYTES)) 18 | 19 | void run_checks(const o1c_test_vector *test) { 20 | o1c_scalar25519_t a, b, c, d, neg_a, neg_b, ab_c_prod_sum, ab_c_prod_diff, reduced; 21 | uint8_t nonreduced[o1c_scalar25519_NONREDUCED_BYTES]; 22 | o1c_hex2bin(a->v, o1c_scalar25519_BYTES, test->a, o1c_scalar25519_BYTES * 2); 23 | o1c_hex2bin(b->v, o1c_scalar25519_BYTES, test->b, o1c_scalar25519_BYTES * 2); 24 | o1c_hex2bin(c->v, o1c_scalar25519_BYTES, test->c, o1c_scalar25519_BYTES * 2); 25 | o1c_hex2bin(neg_a->v, o1c_scalar25519_BYTES, test->neg_a, o1c_scalar25519_BYTES * 2); 26 | o1c_hex2bin(neg_b->v, o1c_scalar25519_BYTES, test->neg_b, o1c_scalar25519_BYTES * 2); 27 | o1c_hex2bin(ab_c_prod_sum->v, o1c_scalar25519_BYTES, test->ab_c_prod_sum, o1c_scalar25519_BYTES * 2); 28 | o1c_hex2bin(ab_c_prod_diff->v, o1c_scalar25519_BYTES, test->ab_c_prod_diff, o1c_scalar25519_BYTES * 2); 29 | o1c_hex2bin(nonreduced, o1c_scalar25519_NONREDUCED_BYTES, test->nonreduced, o1c_scalar25519_NONREDUCED_BYTES * 2); 30 | o1c_hex2bin(reduced->v, o1c_scalar25519_BYTES, test->reduced, o1c_scalar25519_BYTES * 2); 31 | 32 | #ifdef TODO_SIGNCRYPT 33 | o1c_scalar25519_negate(d, a); 34 | assert_eq(d, neg_a); 35 | 36 | o1c_scalar25519_negate(d, b); 37 | assert_eq(d, neg_b); 38 | 39 | o1c_scalar25519_negate(d, c); 40 | o1c_scalar25519_mul_add(d, a, b, d); 41 | assert_eq(d, ab_c_prod_diff); 42 | #endif 43 | 44 | o1c_scalar25519_mul_add(d, a, b, c); 45 | assert_eq(d, ab_c_prod_sum); 46 | 47 | o1c_scalar25519_reduce(d, nonreduced); 48 | assert_eq(d, reduced); 49 | } 50 | 51 | #include "test_scalar25519.txt" 52 | 53 | int main(void) { 54 | for (size_t i = 0; i <= 256; ++i) run_checks(&data[i]); 55 | } 56 | -------------------------------------------------------------------------------- /src/test/c/test_xchacha20poly1305.c: -------------------------------------------------------------------------------- 1 | #include "xchacha20poly1305.h" 2 | #include "util.h" 3 | #include "test.h" 4 | 5 | typedef struct { 6 | size_t ad_len; 7 | size_t pt_len; 8 | char key[o1c_xchacha20poly1305_KEY_BYTES * 2 + 1]; 9 | char nonce[o1c_xchacha20poly1305_NONCE_BYTES * 2 + 1]; 10 | char *ciphertext; 11 | } o1c_test_vector; 12 | 13 | void run_checks(const o1c_test_vector *test) { 14 | uint8_t key[o1c_xchacha20poly1305_KEY_BYTES], nonce[o1c_xchacha20poly1305_NONCE_BYTES]; 15 | uint8_t ad[test->ad_len], pt[test->pt_len]; 16 | init_buf(ad, sizeof ad); 17 | init_buf(pt, sizeof pt); 18 | uint8_t ct[test->pt_len + o1c_xchacha20poly1305_TAG_BYTES]; 19 | o1c_hex2bin(key, o1c_xchacha20poly1305_KEY_BYTES, test->key, o1c_xchacha20poly1305_KEY_BYTES * 2); 20 | o1c_hex2bin(nonce, o1c_xchacha20poly1305_NONCE_BYTES, test->nonce, o1c_xchacha20poly1305_NONCE_BYTES * 2); 21 | o1c_hex2bin(ct, sizeof ct, test->ciphertext, (test->pt_len + o1c_xchacha20poly1305_TAG_BYTES) * 2 + 1); 22 | uint8_t actual[test->pt_len + o1c_xchacha20poly1305_TAG_BYTES]; 23 | o1c_xchacha20poly1305_encrypt(actual, actual + sizeof pt, pt, sizeof pt, ad, sizeof ad, nonce, key); 24 | assert(o1c_mem_eq(ct, actual, sizeof actual)); 25 | } 26 | 27 | #include "test_xchacha20poly1305.txt" 28 | 29 | int main() { 30 | for (size_t i = 0; i <= 32*32; ++i) run_checks(&data[i]); 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/dev/o1c/KeyManagerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c; 22 | 23 | import org.junit.jupiter.api.Test; 24 | 25 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 26 | 27 | class KeyManagerTest { 28 | 29 | private final KeyManager keyManager = KeyManager.getInstance(); 30 | 31 | @Test 32 | void signatureSmokeTest() { 33 | KeyPair keyPair = keyManager.generateKeyPair(); 34 | for (int i = 0; i < 10; i++) { 35 | byte[] message = generateTestVector(i); 36 | assertArrayEquals(message, keyPair.openSignedMessage(keyPair.sign(message))); 37 | } 38 | } 39 | 40 | @Test 41 | void secretBoxSmokeTest() { 42 | SecretKey key = keyManager.generateSecretKey(); 43 | for (int i = 0; i < 10; i++) { 44 | byte[] data = generateTestVector(i); 45 | for (int j = 0; j < 10; j++) { 46 | byte[] context = generateTestVector(j); 47 | assertArrayEquals(data, key.openBox(key.box(data, context), context)); 48 | } 49 | } 50 | } 51 | 52 | @Test 53 | void boxSmokeTest() { 54 | KeyPair alice = keyManager.generateKeyPair(); 55 | KeyPair bob = keyManager.generateKeyPair(); 56 | for (int i = 0; i < 10; i++) { 57 | byte[] message = generateTestVector(i); 58 | for (int j = 0; j < 10; j++) { 59 | byte[] context = generateTestVector(j); 60 | assertArrayEquals(message, bob.openBox(alice, alice.box(bob, message, context), context)); 61 | } 62 | } 63 | } 64 | 65 | @Test 66 | void sealedBoxSmokeTest() { 67 | KeyPair alice = keyManager.generateKeyPair(); 68 | KeyPair bob = keyManager.generateKeyPair(); 69 | for (int i = 0; i < 10; i++) { 70 | byte[] message = generateTestVector(i); 71 | for (int j = 0; j < 10; j++) { 72 | byte[] context = generateTestVector(j); 73 | assertArrayEquals(message, bob.openSealedBox(alice, alice.sealedBox(bob, message, context), context)); 74 | } 75 | } 76 | } 77 | 78 | private byte[] generateTestVector(int length) { 79 | byte[] vector = new byte[length]; 80 | for (int i = 0; i < length; i++) { 81 | vector[i] = (byte) (i % 111); 82 | } 83 | return vector; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/dev/o1c/impl/blake3/Blake3HashTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.impl.blake3; 22 | 23 | import com.fasterxml.jackson.jr.ob.JSON; 24 | import dev.o1c.spi.Hash; 25 | import dev.o1c.spi.HashFactory; 26 | import dev.o1c.util.ByteOps; 27 | import org.junit.jupiter.api.DynamicNode; 28 | import org.junit.jupiter.api.TestFactory; 29 | 30 | import java.io.IOException; 31 | import java.nio.charset.StandardCharsets; 32 | import java.util.ArrayList; 33 | import java.util.Arrays; 34 | import java.util.List; 35 | 36 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 37 | import static org.junit.jupiter.api.DynamicContainer.dynamicContainer; 38 | import static org.junit.jupiter.api.DynamicTest.dynamicTest; 39 | 40 | class Blake3HashTest { 41 | @TestFactory 42 | List testVectors() throws IOException { 43 | JSON json = JSON.builder().enable(JSON.Feature.USE_FIELDS).build(); 44 | TestVector testVector = json.beanFrom(TestVector.class, getClass().getResourceAsStream("test_vectors.json")); 45 | byte[] key = testVector.key.getBytes(StandardCharsets.UTF_8); 46 | byte[] context = testVector.context_string.getBytes(StandardCharsets.UTF_8); 47 | List tests = new ArrayList<>(); 48 | HashFactory hashFactory = Blake3HashFactory.INSTANCE; 49 | Hash hasher = hashFactory.newHash(); 50 | for (Case testCase : testVector.cases) { 51 | byte[] input = new byte[testCase.input_len]; 52 | for (int i = 0; i < input.length; i++) { 53 | input[i] = (byte) (i % 251); 54 | } 55 | byte[] hash = ByteOps.fromHex(testCase.hash); 56 | byte[] truncatedHash = Arrays.copyOf(hash, 32); 57 | byte[] keyedHash = ByteOps.fromHex(testCase.keyed_hash); 58 | byte[] truncatedKeyedHash = Arrays.copyOf(keyedHash, 32); 59 | byte[] deriveKey = ByteOps.fromHex(testCase.derive_key); 60 | byte[] truncatedDeriveKey = Arrays.copyOf(deriveKey, 32); 61 | tests.add(dynamicContainer("input length=" + testCase.input_len, Arrays.asList( 62 | dynamicTest("hash xof", () -> { 63 | hasher.reset(); 64 | hasher.update(input); 65 | byte[] actual = new byte[hash.length]; 66 | hasher.doFinalize(actual); 67 | assertArrayEquals(hash, actual); 68 | }), 69 | dynamicTest("hash 256", 70 | () -> assertArrayEquals(truncatedHash, hasher.hash(input))), 71 | dynamicTest("keyed hash xof", () -> { 72 | Hash blake3 = hashFactory.newKeyedHash(key); 73 | blake3.update(input); 74 | byte[] actual = new byte[keyedHash.length]; 75 | blake3.doFinalize(actual); 76 | assertArrayEquals(keyedHash, actual); 77 | }), 78 | dynamicTest("keyed hash 256", 79 | () -> assertArrayEquals(truncatedKeyedHash, hashFactory.newKeyedHash(key).hash(input))), 80 | dynamicTest("derive key xof", () -> { 81 | Hash blake3 = hashFactory.newKeyDerivationFunction(context); 82 | blake3.update(input); 83 | byte[] actual = new byte[deriveKey.length]; 84 | blake3.doFinalize(actual); 85 | assertArrayEquals(deriveKey, actual); 86 | }), 87 | dynamicTest("derive key 256", 88 | () -> assertArrayEquals(truncatedDeriveKey, hashFactory.newKeyDerivationFunction(context).hash(input))) 89 | ))); 90 | } 91 | return tests; 92 | } 93 | 94 | static class TestVector { 95 | public String key; 96 | public String context_string; 97 | public Case[] cases; 98 | } 99 | 100 | static class Case { 101 | public int input_len; 102 | public String hash; 103 | public String keyed_hash; 104 | public String derive_key; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/test/java/dev/o1c/impl/chacha20/ChaCha20Poly1305CipherTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.impl.chacha20; 22 | 23 | import dev.o1c.spi.Cipher; 24 | import dev.o1c.util.ByteOps; 25 | import org.junit.jupiter.api.Test; 26 | 27 | import java.nio.charset.StandardCharsets; 28 | 29 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 30 | 31 | class ChaCha20Poly1305CipherTest { 32 | @Test 33 | void standardTest() { 34 | byte[] plaintext = 35 | "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it." 36 | .getBytes(StandardCharsets.US_ASCII); 37 | byte[] aad = ByteOps.fromHex("50 51 52 53 c0 c1 c2 c3 c4 c5 c6 c7"); 38 | byte[] key = ByteOps.fromHex("80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f" + 39 | "90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f"); 40 | byte[] nonce = ByteOps.fromHex("07 00 00 00 40 41 42 43 44 45 46 47"); 41 | byte[] ciphertext = ByteOps.fromHex("d3 1a 8d 34 64 8e 60 db 7b 86 af bc 53 ef 7e c2" + 42 | "a4 ad ed 51 29 6e 08 fe a9 e2 b5 a7 36 ee 62 d6" + 43 | "3d be a4 5e 8c a9 67 12 82 fa fb 69 da 92 72 8b" + 44 | "1a 71 de 0a 9e 06 0b 29 05 d6 a5 b6 7e cd 3b 36" + 45 | "92 dd bd 7f 2d 77 8b 8c 98 03 ae e3 28 09 1b 58" + 46 | "fa b3 24 e4 fa d6 75 94 55 85 80 8b 48 31 d7 bc" + 47 | "3f f4 de f0 8e 4b 7a 9d e5 76 d2 65 86 ce c6 4b" + 48 | "61 16"); 49 | byte[] tag = ByteOps.fromHex("1ae10b594f09e26a7e902ecbd0600691"); 50 | 51 | Cipher cipher = new ChaCha20Poly1305Cipher(); 52 | cipher.init(key, nonce, aad); 53 | byte[] actualCiphertext = new byte[ciphertext.length]; 54 | byte[] actualTag = new byte[tag.length]; 55 | cipher.encrypt(plaintext, 0, plaintext.length, actualCiphertext, 0, actualTag, 0); 56 | assertArrayEquals(ciphertext, actualCiphertext); 57 | assertArrayEquals(tag, actualTag); 58 | 59 | byte[] actualPlaintext = new byte[plaintext.length]; 60 | cipher.init(key, nonce, aad); 61 | cipher.decrypt(ciphertext,0, ciphertext.length, tag, 0, actualPlaintext, 0); 62 | assertArrayEquals(plaintext, actualPlaintext); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/dev/o1c/impl/chacha20/ChaCha20Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.impl.chacha20; 22 | 23 | import dev.o1c.util.ByteOps; 24 | import org.junit.jupiter.api.Test; 25 | 26 | import java.nio.charset.StandardCharsets; 27 | 28 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 29 | 30 | class ChaCha20Test { 31 | private static final String PLAINTEXT = 32 | "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; 33 | private static final byte[] CIPHERTEXT = 34 | ByteOps.fromHex("6e 2e 35 9a 25 68 f9 80 41 ba 07 28 dd 0d 69 81" + 35 | "e9 7e 7a ec 1d 43 60 c2 0a 27 af cc fd 9f ae 0b" + 36 | "f9 1b 65 c5 52 47 33 ab 8f 59 3d ab cd 62 b3 57" + 37 | "16 39 d6 24 e6 51 52 ab 8f 53 0c 35 9f 08 61 d8" + 38 | "07 ca 0d bf 50 0d 6a 61 56 a3 8e 08 8a 22 b6 5e" + 39 | "52 bc 51 4d 16 cc f8 06 81 8c e9 1a b7 79 37 36" + 40 | "5a f9 0b bf 74 a3 5b e6 b4 0b 8e ed f2 78 5e 42" + 41 | "87 4d"); 42 | 43 | @Test 44 | void standardTest() { 45 | byte[] key = new byte[32]; 46 | for (int i = 0; i < key.length; i++) { 47 | key[i] = (byte) i; 48 | } 49 | byte[] nonce = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0x4a, 0, 0, 0, 0 }; 50 | ChaCha20 chaCha20 = new ChaCha20(); 51 | chaCha20.initKey(key); 52 | chaCha20.initNonce(nonce); 53 | chaCha20.initCounter(1); 54 | byte[] plaintext = PLAINTEXT.getBytes(StandardCharsets.UTF_8); 55 | byte[] ciphertext = new byte[plaintext.length]; 56 | chaCha20.crypt(plaintext, 0, plaintext.length, ciphertext, 0); 57 | assertArrayEquals(CIPHERTEXT, ciphertext); 58 | 59 | chaCha20.initCounter(1); 60 | byte[] decrypted = new byte[plaintext.length]; 61 | chaCha20.crypt(ciphertext, 0, ciphertext.length, decrypted, 0); 62 | assertArrayEquals(plaintext, decrypted); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/dev/o1c/impl/chacha20/Poly1305Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.impl.chacha20; 22 | 23 | import dev.o1c.util.ByteOps; 24 | import org.junit.jupiter.api.Test; 25 | 26 | import java.nio.charset.StandardCharsets; 27 | 28 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 29 | 30 | class Poly1305Test { 31 | @Test 32 | void standardTest() { 33 | byte[] key = ByteOps.fromHex("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b"); 34 | byte[] mac = ByteOps.fromHex("a8061dc1305136c6c22b8baf0c0127a9"); 35 | byte[] message = "Cryptographic Forum Research Group".getBytes(StandardCharsets.US_ASCII); 36 | 37 | Poly1305 poly1305 = new Poly1305(); 38 | poly1305.init(key); 39 | poly1305.update(message, 0, message.length); 40 | assertArrayEquals(mac, poly1305.computeMac()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/dev/o1c/impl/chacha20/XChaCha20Poly1305CipherTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.impl.chacha20; 22 | 23 | import dev.o1c.spi.CipherTest; 24 | import org.junit.jupiter.api.DynamicNode; 25 | import org.junit.jupiter.api.TestFactory; 26 | 27 | import java.util.List; 28 | 29 | class XChaCha20Poly1305CipherTest { 30 | @TestFactory 31 | List loadTestVectors() { 32 | return CipherTest.loadAEADTests("xchacha20poly1305.txt.gz", new XChaCha20Poly1305Cipher()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/dev/o1c/lwc/NistLwcTestVectors.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.lwc; 22 | 23 | import dev.o1c.spi.Cipher; 24 | import dev.o1c.spi.CipherTest; 25 | import dev.o1c.spi.CryptoHashTest; 26 | import dev.o1c.spi.Hash; 27 | import org.junit.jupiter.api.DynamicNode; 28 | 29 | import java.util.List; 30 | 31 | // https://csrc.nist.gov/projects/lightweight-cryptography/round-2-candidates 32 | // test vectors can be regenerated from reference implementations 33 | public class NistLwcTestVectors { 34 | public static List loadHashTestVectors(Hash hash) { 35 | String filename = String.format("LWC_HASH_KAT_%d.txt.gz", hash.hashLength() * Byte.SIZE); 36 | return CryptoHashTest.loadHashTests(filename, hash); 37 | } 38 | 39 | public static List loadAEADTestVectors(Cipher cipher) { 40 | String filename = String.format("LWC_AEAD_KAT_%d_128.txt.gz", cipher.keyLength() * Byte.SIZE); 41 | return CipherTest.loadAEADTests(filename, cipher); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/dev/o1c/lwc/ascon/AsconCipherTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.lwc.ascon; 22 | 23 | import dev.o1c.lwc.NistLwcTestVectors; 24 | import org.junit.jupiter.api.DynamicNode; 25 | import org.junit.jupiter.api.TestFactory; 26 | 27 | import java.util.List; 28 | 29 | class AsconCipherTest { 30 | @TestFactory 31 | List testVectors() { 32 | return NistLwcTestVectors.loadAEADTestVectors(new AsconCipher()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/dev/o1c/lwc/ascon/AsconHashTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.lwc.ascon; 22 | 23 | import dev.o1c.lwc.NistLwcTestVectors; 24 | import org.junit.jupiter.api.DynamicNode; 25 | import org.junit.jupiter.api.TestFactory; 26 | 27 | import java.util.List; 28 | 29 | class AsconHashTest { 30 | @TestFactory 31 | List testVectors() { 32 | return NistLwcTestVectors.loadHashTestVectors(new AsconHash(32)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/dev/o1c/lwc/xoodyak/XoodooTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.lwc.xoodyak; 22 | 23 | import dev.o1c.util.ByteOps; 24 | import org.junit.jupiter.api.DynamicNode; 25 | import org.junit.jupiter.api.TestFactory; 26 | 27 | import java.io.BufferedReader; 28 | import java.io.IOException; 29 | import java.io.InputStream; 30 | import java.io.InputStreamReader; 31 | import java.nio.charset.StandardCharsets; 32 | import java.util.ArrayList; 33 | import java.util.List; 34 | import java.util.regex.Matcher; 35 | import java.util.regex.Pattern; 36 | import java.util.zip.GZIPInputStream; 37 | 38 | import static org.junit.jupiter.api.Assertions.*; 39 | import static org.junit.jupiter.api.DynamicTest.dynamicTest; 40 | 41 | class XoodooTest { 42 | 43 | /* 44 | test vectors generated using reference Xoodyak C source code and the following generator code: 45 | 46 | #include 47 | #include "Xoodoo-SnP.h" 48 | 49 | void print_bstr(const char *label, const unsigned char *data, unsigned long long length) { 50 | printf("%s", label); 51 | for (unsigned long long i = 0; i < length; i++) { 52 | printf("%02X", data[i]); 53 | } 54 | printf("\n"); 55 | } 56 | 57 | int main() { 58 | unsigned char initial_state[48]; 59 | for (unsigned long long i = 0; i < sizeof initial_state; i++) { 60 | initial_state[i] = i; 61 | } 62 | unsigned char state[48]; 63 | Xoodoo_Initialize(state); 64 | for (unsigned long long i = 0; i < 1024; i++) { 65 | for (unsigned long long j = 0; j < i; j++) { 66 | ++initial_state[j % 48]; 67 | } 68 | printf("Count = %ull\n", i + 1); 69 | print_bstr("Start = ", initial_state, sizeof initial_state); 70 | Xoodoo_OverwriteBytes(state, initial_state, 0, sizeof initial_state); 71 | Xoodoo_Permute_12rounds(state); 72 | print_bstr("End = ", state, sizeof state); 73 | printf("\n"); 74 | } 75 | } 76 | */ 77 | 78 | @TestFactory 79 | List loadTests() { 80 | List tests = new ArrayList<>(1024); 81 | InputStream stream = getClass().getResourceAsStream("xoodoo.txt.gz"); 82 | assertNotNull(stream); 83 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(stream), StandardCharsets.UTF_8))) { 84 | Pattern countLine = Pattern.compile("Count = (\\d+)"); 85 | Pattern startLine = Pattern.compile("Start = ([0-9A-F]{96})"); 86 | Pattern endLine = Pattern.compile("End = ([0-9A-F]{96})"); 87 | Xoodoo xoodoo = new Xoodoo(); 88 | for (int i = 0; i < 1024; i++) { 89 | Matcher matcher = countLine.matcher(reader.readLine()); 90 | assertTrue(matcher.matches()); 91 | String count = matcher.group(1); 92 | matcher = startLine.matcher(reader.readLine()); 93 | assertTrue(matcher.matches()); 94 | byte[] start = ByteOps.fromHex(matcher.group(1)); 95 | matcher = endLine.matcher(reader.readLine()); 96 | assertTrue(matcher.matches()); 97 | byte[] end = ByteOps.fromHex(matcher.group(1)); 98 | byte[] actual = new byte[end.length]; 99 | // blank line 100 | reader.readLine(); 101 | tests.add(dynamicTest("state#" + count, () -> { 102 | xoodoo.reset(); 103 | xoodoo.addBytes(start, 0, start.length); 104 | xoodoo.permute(); 105 | xoodoo.extractBytes(actual, 0, actual.length); 106 | assertArrayEquals(end, actual); 107 | })); 108 | } 109 | } catch (IOException e) { 110 | fail(e); 111 | } 112 | return tests; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/test/java/dev/o1c/lwc/xoodyak/XoodyakCipherTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.lwc.xoodyak; 22 | 23 | import dev.o1c.lwc.NistLwcTestVectors; 24 | import org.junit.jupiter.api.DynamicNode; 25 | import org.junit.jupiter.api.TestFactory; 26 | 27 | import java.util.List; 28 | 29 | class XoodyakCipherTest { 30 | @TestFactory 31 | List testVectors() { 32 | return NistLwcTestVectors.loadAEADTestVectors(new XoodyakCipher()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/dev/o1c/lwc/xoodyak/XoodyakHashTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.lwc.xoodyak; 22 | 23 | import dev.o1c.lwc.NistLwcTestVectors; 24 | import dev.o1c.spi.HashFactory; 25 | import org.junit.jupiter.api.DynamicNode; 26 | import org.junit.jupiter.api.TestFactory; 27 | 28 | import java.util.List; 29 | 30 | class XoodyakHashTest { 31 | @TestFactory 32 | List testVectors() { 33 | HashFactory hashFactory = new XoodyakHashFactory(); 34 | return NistLwcTestVectors.loadHashTestVectors(hashFactory.newHash()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/dev/o1c/spi/CipherTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.spi; 22 | 23 | import dev.o1c.util.ByteOps; 24 | import org.jetbrains.annotations.NotNull; 25 | import org.junit.jupiter.api.DynamicNode; 26 | 27 | import java.io.BufferedReader; 28 | import java.io.IOException; 29 | import java.io.InputStream; 30 | import java.io.InputStreamReader; 31 | import java.nio.charset.StandardCharsets; 32 | import java.util.ArrayList; 33 | import java.util.Arrays; 34 | import java.util.List; 35 | import java.util.regex.Matcher; 36 | import java.util.regex.Pattern; 37 | import java.util.zip.GZIPInputStream; 38 | 39 | import static org.junit.jupiter.api.Assertions.*; 40 | import static org.junit.jupiter.api.DynamicContainer.dynamicContainer; 41 | import static org.junit.jupiter.api.DynamicTest.dynamicTest; 42 | 43 | public class CipherTest { 44 | public static @NotNull List loadAEADTests(@NotNull String testResource, @NotNull Cipher cipher) { 45 | List vectors = new ArrayList<>(1088); 46 | InputStream stream = cipher.getClass().getResourceAsStream(testResource); 47 | assertNotNull(stream, "No test resource found: " + testResource); 48 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(stream), StandardCharsets.UTF_8))) { 49 | Pattern countLine = Pattern.compile("Count = (\\d+)"); 50 | Pattern keyLine = Pattern.compile("Key = ([0-9A-F]+)"); 51 | Pattern nonceLine = Pattern.compile("Nonce = ([0-9A-F]+)"); 52 | Pattern ptLine = Pattern.compile("PT = ([0-9A-F]*)"); 53 | Pattern adLine = Pattern.compile("AD = ([0-9A-F]*)"); 54 | Pattern ctLine = Pattern.compile("CT = ([0-9A-F]+)"); // concatenates ciphertext and tag 55 | for (int i = 0; i < 1088; i++) { 56 | Matcher matcher = countLine.matcher(reader.readLine()); 57 | assertTrue(matcher.matches()); 58 | String count = matcher.group(1); 59 | matcher = keyLine.matcher(reader.readLine()); 60 | assertTrue(matcher.matches()); 61 | byte[] key = ByteOps.fromHex(matcher.group(1)); 62 | matcher = nonceLine.matcher(reader.readLine()); 63 | assertTrue(matcher.matches()); 64 | byte[] nonce = ByteOps.fromHex(matcher.group(1)); 65 | matcher = ptLine.matcher(reader.readLine()); 66 | assertTrue(matcher.matches()); 67 | String pt = matcher.group(1); 68 | byte[] plaintext = ByteOps.fromHex(pt); 69 | matcher = adLine.matcher(reader.readLine()); 70 | assertTrue(matcher.matches()); 71 | String ad = matcher.group(1); 72 | byte[] context = ByteOps.fromHex(ad); 73 | matcher = ctLine.matcher(reader.readLine()); 74 | assertTrue(matcher.matches()); 75 | String ct = matcher.group(1); 76 | byte[] ciphertext = ByteOps.fromHex(ct); 77 | reader.readLine(); // empty line 78 | vectors.add(dynamicContainer("P[" + pt + "]A[" + ad + "]", Arrays.asList( 79 | dynamicTest("encrypt", 80 | () -> assertArrayEquals(ciphertext, cipher.encrypt(key, nonce, context, plaintext))), 81 | dynamicTest("decrypt", 82 | () -> assertArrayEquals(plaintext, cipher.decrypt(key, nonce, context, ciphertext))) 83 | ))); 84 | } 85 | } catch (IOException e) { 86 | fail(e); 87 | } 88 | return vectors; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/test/java/dev/o1c/spi/CryptoHashTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.spi; 22 | 23 | import dev.o1c.util.ByteOps; 24 | import org.jetbrains.annotations.NotNull; 25 | import org.junit.jupiter.api.DynamicNode; 26 | 27 | import java.io.BufferedReader; 28 | import java.io.IOException; 29 | import java.io.InputStream; 30 | import java.io.InputStreamReader; 31 | import java.nio.charset.StandardCharsets; 32 | import java.util.ArrayList; 33 | import java.util.Arrays; 34 | import java.util.List; 35 | import java.util.regex.Matcher; 36 | import java.util.regex.Pattern; 37 | import java.util.zip.GZIPInputStream; 38 | 39 | import static org.junit.jupiter.api.Assertions.*; 40 | import static org.junit.jupiter.api.DynamicContainer.dynamicContainer; 41 | import static org.junit.jupiter.api.DynamicTest.dynamicTest; 42 | 43 | public class CryptoHashTest { 44 | public static @NotNull List loadHashTests(@NotNull String testResource, @NotNull Hash hash) { 45 | List vectors = new ArrayList<>(1024); 46 | InputStream stream = hash.getClass().getResourceAsStream(testResource); 47 | assertNotNull(stream, "No test resource found: " + testResource); 48 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(stream), StandardCharsets.UTF_8))) { 49 | Pattern countLine = Pattern.compile("Count = (\\d+)"); 50 | Pattern msgLine = Pattern.compile("Msg = ([0-9A-F]*)"); 51 | Pattern hashLine = Pattern.compile("MD = ([0-9A-F]{64})"); 52 | for (int i = 0; i < 1024; i++) { 53 | Matcher matcher = countLine.matcher(reader.readLine()); 54 | assertTrue(matcher.matches()); 55 | String count = matcher.group(1); 56 | matcher = msgLine.matcher(reader.readLine()); 57 | assertTrue(matcher.matches()); 58 | String msg = matcher.group(1); 59 | byte[] message = ByteOps.fromHex(msg); 60 | matcher = hashLine.matcher(reader.readLine()); 61 | assertTrue(matcher.matches()); 62 | byte[] md = ByteOps.fromHex(matcher.group(1)); 63 | reader.readLine(); // empty line 64 | vectors.add(dynamicContainer("M[" + msg + "]", Arrays.asList( 65 | dynamicTest("hash", () -> assertArrayEquals(md, hash.hash(message))), 66 | dynamicTest("reset/update/finish", () -> { 67 | hash.reset(); 68 | hash.update(message); 69 | assertArrayEquals(md, hash.doFinalize()); 70 | }) 71 | // TODO: test out hashing per-byte and other partial hashing 72 | ))); 73 | } 74 | } catch (IOException e) { 75 | fail(e); 76 | } 77 | return vectors; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/dev/o1c/util/ByteOpsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2021, Matt Sicker 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * SPDX-License-Identifier: ISC 19 | */ 20 | 21 | package dev.o1c.util; 22 | 23 | import org.junit.jupiter.api.Test; 24 | 25 | import java.util.Random; 26 | 27 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 28 | 29 | class ByteOpsTest { 30 | @Test 31 | void hexRoundTrip() { 32 | Random random = new Random(42); 33 | byte[] data = new byte[random.nextInt(1024) + 1]; 34 | random.nextBytes(data); 35 | assertArrayEquals(data, ByteOps.fromHex(ByteOps.toHex(data))); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/resources/dev/o1c/impl/chacha20/xchacha20poly1305.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o1c-dev/o1c/9ea4f63affc5ce5695e22c8d82889b8b09d23862/src/test/resources/dev/o1c/impl/chacha20/xchacha20poly1305.txt.gz -------------------------------------------------------------------------------- /src/test/resources/dev/o1c/lwc/ascon/LWC_AEAD_KAT_128_128.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o1c-dev/o1c/9ea4f63affc5ce5695e22c8d82889b8b09d23862/src/test/resources/dev/o1c/lwc/ascon/LWC_AEAD_KAT_128_128.txt.gz -------------------------------------------------------------------------------- /src/test/resources/dev/o1c/lwc/ascon/LWC_HASH_KAT_256.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o1c-dev/o1c/9ea4f63affc5ce5695e22c8d82889b8b09d23862/src/test/resources/dev/o1c/lwc/ascon/LWC_HASH_KAT_256.txt.gz -------------------------------------------------------------------------------- /src/test/resources/dev/o1c/lwc/xoodyak/LWC_AEAD_KAT_128_128.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o1c-dev/o1c/9ea4f63affc5ce5695e22c8d82889b8b09d23862/src/test/resources/dev/o1c/lwc/xoodyak/LWC_AEAD_KAT_128_128.txt.gz -------------------------------------------------------------------------------- /src/test/resources/dev/o1c/lwc/xoodyak/LWC_HASH_KAT_256.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o1c-dev/o1c/9ea4f63affc5ce5695e22c8d82889b8b09d23862/src/test/resources/dev/o1c/lwc/xoodyak/LWC_HASH_KAT_256.txt.gz -------------------------------------------------------------------------------- /src/test/resources/dev/o1c/lwc/xoodyak/xoodoo.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o1c-dev/o1c/9ea4f63affc5ce5695e22c8d82889b8b09d23862/src/test/resources/dev/o1c/lwc/xoodyak/xoodoo.txt.gz --------------------------------------------------------------------------------