├── src ├── iota │ ├── .gitignore │ ├── iota_types.h │ ├── addresses.h │ ├── transfers.h │ ├── kerl.c │ ├── signing.h │ ├── signing.c │ ├── kerl.h │ ├── common.h │ ├── addresses.c │ ├── bundle.h │ ├── conversion.h │ ├── transfers.c │ ├── bundle.c │ └── conversion.c ├── keccak │ ├── macros.h │ ├── options.h │ ├── sha3.h │ └── sha3.c ├── aux.c ├── aux.h ├── main.h └── main.c ├── activate_virt_env.sh ├── .gitattributes ├── .gitmodules ├── tests ├── hash_file.h ├── .gitignore ├── transaction_file.h ├── test_common.h ├── hash_file.c ├── cmake │ └── modules │ │ └── FindCMocka.cmake ├── test_mocks.c ├── api_tests.h ├── transaction_file.c ├── CMakeLists.txt ├── set_seed_test.c ├── pubkey_test.c ├── address_test.c ├── kerl_test.c ├── sign_test.c ├── conversion_test.c ├── test_vectors.h ├── bundle_test.c ├── signing_test.c └── tx_test.c ├── demos ├── nodejs │ └── README.md └── python │ ├── demo.py │ └── test_main_tx.py ├── .travis.yml ├── .gitignore ├── CMakeLists.txt ├── .clang-format ├── LICENSE ├── README.md └── install_dev_env.sh /src/iota/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.d 3 | -------------------------------------------------------------------------------- /activate_virt_env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | /bin/bash --rcfile .pyenv/bin/activate 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tests/kerl-spec"] 2 | path = tests/kerl-spec 3 | url = https://github.com/iotaledger/kerl 4 | -------------------------------------------------------------------------------- /src/keccak/macros.h: -------------------------------------------------------------------------------- 1 | #ifndef __MACROS_H__ 2 | #define __MACROS_H__ 3 | 4 | #define MEMSET_BZERO(p,l) memset((p), 0, (l)) 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /tests/hash_file.h: -------------------------------------------------------------------------------- 1 | #ifndef HASH_FILE_H 2 | #define HASH_FILE_H 3 | 4 | void test_for_each_line(const char *file_name, void (*test)(char **)); 5 | 6 | #endif // HASH_FILE_H 7 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | # CMake 2 | CMakeCache.txt 3 | CMakeFiles 4 | CMakeScripts 5 | Testing 6 | Makefile 7 | cmake_install.cmake 8 | install_manifest.txt 9 | compile_commands.json 10 | CTestTestfile.cmake 11 | *_test 12 | -------------------------------------------------------------------------------- /demos/nodejs/README.md: -------------------------------------------------------------------------------- 1 | # NodeJS Demo 2 | 3 | Currently, there is no separate demo for trying IOTA using NodeJS and Ledger Nano S. Please go to the [special IOTA-branch on our ledgerjs-fork](https://github.com/IOTA-Ledger/ledgerjs/tree/iota-js) to run the tests there. 4 | -------------------------------------------------------------------------------- /tests/transaction_file.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSACTION_FILE_H 2 | #define TRANSACTION_FILE_H 3 | 4 | #include "api.h" 5 | 6 | #define SIGNATURE_LENGTH 4374 7 | 8 | void test_for_each_bundle(const char *file_name, 9 | void (*test)(char *, TX_INPUT *, char *, 10 | char[][SIGNATURE_LENGTH])); 11 | 12 | #endif // TRANSACTION_FILE_H 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | sudo: required 3 | install: 4 | # the cmocka package coming with trusty is not up to date 5 | - wget https://cmocka.org/files/1.1/cmocka-1.1.0.tar.xz 6 | - tar -xf cmocka-1.1.0.tar.xz 7 | - cd cmocka-1.1.0 8 | - mkdir build 9 | - cd build 10 | - cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug .. && make && sudo make install 11 | - cd ../.. 12 | - rm -rf cmocka-1.1.0 13 | script: 14 | - cd tests && mkdir build && cd build && cmake .. && make && make test 15 | -------------------------------------------------------------------------------- /src/iota/iota_types.h: -------------------------------------------------------------------------------- 1 | #ifndef IOTA_TYPES_H 2 | #define IOTA_TYPES_H 3 | 4 | #include 5 | 6 | typedef int8_t trit_t; 7 | typedef int8_t tryte_t; 8 | 9 | #define MIN_TRIT_VALUE -1 10 | #define MAX_TRIT_VALUE 1 11 | 12 | #define MIN_TRYTE_VALUE -13 13 | #define MAX_TRYTE_VALUE 13 14 | 15 | #define MAX_IOTA_VALUE INT64_C(2779530283277761) // (3^33-1) / 2 16 | 17 | #define MIN_SECURITY_LEVEL 1 18 | #define MAX_SECURITY_LEVEL 3 19 | 20 | #define NUM_HASH_TRITS 243 21 | #define NUM_HASH_TRYTES 81 22 | #define NUM_HASH_BYTES (CX_KECCAK384_SIZE) 23 | 24 | #endif // IOTA_TYPES_H 25 | -------------------------------------------------------------------------------- /src/iota/addresses.h: -------------------------------------------------------------------------------- 1 | #ifndef ADDRESSES_H 2 | #define ADDRESSES_H 3 | 4 | #include "iota_types.h" 5 | 6 | void get_public_addr(const unsigned char *seed_bytes, uint32_t idx, 7 | unsigned int security, unsigned char *address_bytes); 8 | 9 | /** @brief Computes the full address string in base-27 encoding. 10 | * The full address consists of the actual address (81 chars) plus 9 chars of 11 | * checksum. 12 | */ 13 | void get_address_with_checksum(const unsigned char *address_bytes, 14 | char *full_address); 15 | 16 | #endif // ADDRESSES_H 17 | -------------------------------------------------------------------------------- /src/iota/transfers.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSFERS_H 2 | #define TRANSFERS_H 3 | 4 | #include 5 | 6 | typedef struct TX_OUTPUT { 7 | char address[81]; 8 | int64_t value; 9 | char message[2187]; 10 | char tag[27]; 11 | } TX_OUTPUT; 12 | 13 | typedef struct TX_INPUT { 14 | int64_t balance; 15 | uint32_t key_index; 16 | } TX_INPUT; 17 | 18 | void prepare_transfers(char *seed, uint8_t security, TX_OUTPUT *outputs, 19 | int num_outputs, TX_INPUT *inputs, int num_inputs, 20 | char transaction_chars[][2673]); 21 | 22 | #endif //TRANSFERS_H 23 | -------------------------------------------------------------------------------- /src/aux.c: -------------------------------------------------------------------------------- 1 | #include "aux.h" 2 | #include 3 | #include "iota/common.h" 4 | 5 | #define PAD_CHAR '9' 6 | 7 | bool validate_chars(const char *chars, unsigned int num_chars) 8 | { 9 | const size_t len = strnlen(chars, num_chars); 10 | for (unsigned int i = 0; i < len; i++) { 11 | const char c = chars[i]; 12 | if (c != '9' && (c < 'A' || c > 'Z')) { 13 | return false; 14 | } 15 | } 16 | 17 | return true; 18 | } 19 | 20 | void rpad_chars(char *destination, const char *source, unsigned int num_chars) 21 | { 22 | const size_t len = strnlen(source, num_chars); 23 | os_memcpy(destination, source, len); 24 | os_memset(destination + len, PAD_CHAR, num_chars - len); 25 | } 26 | -------------------------------------------------------------------------------- /src/aux.h: -------------------------------------------------------------------------------- 1 | #ifndef AUX_H 2 | #define AUX_H 3 | 4 | #include 5 | 6 | /** @brief Checks whether a given string is a valid base-27 encoding. 7 | * Input is checked until num_chars or zero character is reached. 8 | * @param chars string to check 9 | * @param num_chars number of expected charecters 10 | */ 11 | bool validate_chars(const char *chars, unsigned int num_chars); 12 | 13 | /** @brief Copies chars and adds padding if shorter than the expected length. 14 | * @param destination target string 15 | * @param source string to copy and pad 16 | * @param num_chars number of expected charecters 17 | */ 18 | void rpad_chars(char *destination, const char *source, unsigned int num_chars); 19 | 20 | #endif // AUX_H 21 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_H 2 | #define MAIN_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // IOTA-Related APIs 9 | #define INS_GET_PUBKEY 0x01 10 | #define INS_BAD_PUBKEY 0x02 11 | #define INS_GOOD_PUBKEY 0x04 12 | #define INS_CHANGE_INDEX 0x08 13 | #define INS_SIGN 0x10 14 | #define INS_GET_MULTI_SEND 0x20 15 | 16 | //very last chunk or will there be more? 17 | #define TX_MORE 0x00 18 | #define TX_END 0x01 19 | 20 | #define TX_ADDR 0x01 21 | #define TX_VAL 0x02 22 | #define TX_TAG 0x04 23 | #define TX_TIME 0x08 24 | #define TX_CUR 0x10 25 | #define TX_LAST 0x20 26 | 27 | // all of the fields for the tx are filled 28 | #define TX_FULL 0x3F 29 | // end IOTA-Related APIs 30 | 31 | 32 | #endif // MAIN_H 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | debug/ 3 | 4 | build/ 5 | 6 | # Prerequisites 7 | *.d 8 | 9 | # Object files 10 | *.o 11 | *.ko 12 | *.obj 13 | *.elf 14 | 15 | # Linker output 16 | *.ilk 17 | *.map 18 | *.exp 19 | 20 | # Precompiled Headers 21 | *.gch 22 | *.pch 23 | 24 | # Libraries 25 | *.lib 26 | *.a 27 | *.la 28 | *.lo 29 | 30 | # Shared objects (inc. Windows DLLs) 31 | *.dll 32 | *.so 33 | *.so.* 34 | *.dylib 35 | 36 | # Executables 37 | *.exe 38 | *.out 39 | *.app 40 | *.i*86 41 | *.x86_64 42 | *.hex 43 | 44 | # Debug files 45 | *.dSYM/ 46 | *.su 47 | *.idb 48 | *.pdb 49 | 50 | # Kernel Module Compile Results 51 | *.mod* 52 | *.cmd 53 | .tmp_versions/ 54 | modules.order 55 | Module.symvers 56 | Mkfile.old 57 | dkms.conf 58 | 59 | #CMake 60 | CMakeCache.txt 61 | CMakeFiles/ 62 | cmake-build-debug/ 63 | cmake_install.cmake 64 | 65 | #Compiled sources 66 | c_light_wallet 67 | c_light_wallet.cbp 68 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(c_light_wallet) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | set(CMAKE_C_STANDARD 99) 7 | set(CMAKE_C_STANDARD_REQUIRED TRUE) 8 | 9 | set(SOURCE_FILES 10 | src/iota/addresses.c 11 | src/iota/addresses.h 12 | src/iota/bundle.c 13 | src/iota/bundle.h 14 | src/iota/signing.c 15 | src/iota/signing.h 16 | src/iota/common.h 17 | src/iota/conversion.c 18 | src/iota/conversion.h 19 | src/iota/iota_types.h 20 | src/iota/kerl.c 21 | src/iota/kerl.h 22 | src/iota/transfers.h 23 | src/iota/transfers.c 24 | src/keccak/macros.h 25 | src/keccak/options.h 26 | src/keccak/sha3.c 27 | src/keccak/sha3.h 28 | src/aux.c 29 | src/aux.h 30 | src/main.c 31 | src/main.h) 32 | 33 | add_executable(c_light_wallet ${SOURCE_FILES}) 34 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | # BasedOnStyle 3 | 4 | TabWidth: 8 5 | UseTab: Never 6 | IndentWidth: 4 7 | ColumnLimit: 80 8 | MaxEmptyLinesToKeep: 2 9 | 10 | # Break after function definitions and else 11 | BreakBeforeBraces: Stroustrup 12 | 13 | AlignAfterOpenBracket: true 14 | AlignEscapedNewlinesLeft: false 15 | AlignOperands: true 16 | AlwaysBreakAfterReturnType: None 17 | BinPackArguments: true 18 | BinPackParameters: true 19 | DerivePointerAlignment: false 20 | PointerAlignment: Right 21 | SortIncludes: false 22 | SpaceInEmptyParentheses: false 23 | SpacesInCStyleCastParentheses: false 24 | SpacesInParentheses: false 25 | SpacesInSquareBrackets: false 26 | 27 | # Never allow single line statements 28 | AllowShortFunctionsOnASingleLine: false 29 | AllowShortBlocksOnASingleLine: false 30 | AllowShortCaseLabelsOnASingleLine: false 31 | AllowShortIfStatementsOnASingleLine: false 32 | AllowShortLoopsOnASingleLine: false 33 | 34 | -------------------------------------------------------------------------------- /tests/test_common.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_COMMON_H 2 | #define TEST_COMMON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | // include for safe redefines 9 | #include 10 | #include "common.h" 11 | 12 | #include "iota_types.h" 13 | 14 | #ifndef KERL_TEST_FOLDER 15 | #define KERL_TEST_FOLDER "kerl-spec/test_vectors" 16 | #endif 17 | 18 | #ifndef TEST_FOLDER 19 | #define TEST_FOLDER "test_vectors" 20 | #endif 21 | 22 | #define MAX_NUM_HASHES 5 23 | 24 | #define NUM_TRYTES(X) (((X) / NUM_HASH_TRITS) * NUM_HASH_TRYTES) 25 | #define NUM_BYTES(X) (((X) / NUM_HASH_TRITS) * NUM_HASH_BYTES) 26 | 27 | #define MAX_NUM_TRITS (NUM_HASH_TRITS * MAX_NUM_HASHES) 28 | #define MAX_NUM_BYTES NUM_BYTES(MAX_NUM_TRITS) 29 | #define TRITS_PER_TRYTE 3 30 | #define MAX_NUM_TRYTES (MAX_NUM_TRITS / TRITS_PER_TRYTE) 31 | 32 | #undef assert 33 | #define assert(X) mock_assert((int)(X), #X, __FILE__, __LINE__) 34 | 35 | #endif // TEST_COMMON_H 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Tyler, Peter Willemsen and Contributors 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: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | #include 4 | #include 5 | // iota-related stuff 6 | #include "iota/kerl.h" 7 | #include "iota/conversion.h" 8 | #include "iota/addresses.h" 9 | 10 | int print_help(); 11 | void address(char* seedChars, int index, int security, char* result); 12 | 13 | int main(int argc, char *argv[]){ 14 | 15 | if (argc != 5) { 16 | return print_help(); 17 | } 18 | 19 | int num; 20 | int security = (int)strtol(argv[2], NULL, 10); 21 | int index = (int)strtol(argv[3], NULL, 10); 22 | int count = (int)strtol(argv[4], NULL, 10); 23 | char* seed_chars = argv[1]; 24 | 25 | char addresses[(count*82)]; 26 | addresses[0] = '\0'; 27 | for (int i = 0; i < count; i++) { 28 | char char_address[81]; 29 | address(seed_chars, index+i, security, char_address); 30 | strcat(addresses, char_address); 31 | strcat(addresses, "\n"); 32 | } 33 | 34 | 35 | printf("%s", addresses); 36 | 37 | return 0; 38 | } 39 | 40 | int print_help() { 41 | printf("Usage c_light_wallet SECURITY INDEX COUNT\n"); 42 | return 0; 43 | } 44 | 45 | void address(char* seed_chars, int index, int security, char result[81]) { 46 | 47 | unsigned char address[81]; 48 | unsigned char seed_bytes[48]; 49 | chars_to_bytes(seed_chars, seed_bytes, 81); 50 | get_public_addr(seed_bytes, index, security, address); 51 | 52 | bytes_to_chars(address, result, 48); 53 | } 54 | -------------------------------------------------------------------------------- /tests/hash_file.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "test_common.h" 5 | 6 | #define BUFFER_LEN 1024 7 | 8 | static void extract_hashes(char *string, char *hashes[], size_t num_hashes) 9 | { 10 | 11 | char *pch = strtok(string, ","); 12 | for (size_t i = 0; i < num_hashes; i++) { 13 | 14 | if (pch == NULL) { 15 | hashes[i] = NULL; 16 | continue; 17 | } 18 | 19 | hashes[i] = pch; 20 | 21 | pch = strtok(NULL, ","); 22 | } 23 | } 24 | 25 | void test_for_each_line(const char *file_name, void (*test)(char **)) 26 | { 27 | char path_name[BUFFER_LEN]; 28 | snprintf(path_name, sizeof(path_name), "%s/%s", KERL_TEST_FOLDER, file_name); 29 | 30 | FILE *file; 31 | if ((file = fopen(path_name, "r")) == NULL) { 32 | fail_msg("Could not open file."); 33 | } 34 | 35 | char line[BUFFER_LEN]; 36 | unsigned int line_num = 0; 37 | while (fgets(line, sizeof(line), file) != NULL) { 38 | 39 | if (++line_num == 1) { 40 | continue; 41 | } 42 | 43 | // remove trailing new line 44 | char *pos; 45 | if ((pos = strchr(line, '\n')) != NULL) { 46 | *pos = '\0'; 47 | } 48 | else { 49 | fail_msg("Line longer than buffer."); 50 | } 51 | 52 | char *hashes[MAX_NUM_HASHES]; 53 | 54 | extract_hashes(line, hashes, MAX_NUM_HASHES); 55 | 56 | test(hashes); 57 | } 58 | 59 | fclose(file); 60 | } 61 | -------------------------------------------------------------------------------- /tests/cmake/modules/FindCMocka.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find CMocka 2 | # Once done this will define 3 | # 4 | # CMOCKA_ROOT_DIR - Set this variable to the root installation of CMocka 5 | # 6 | # Read-Only variables: 7 | # CMOCKA_FOUND - system has CMocka 8 | # CMOCKA_INCLUDE_DIR - the CMocka include directory 9 | # CMOCKA_LIBRARIES - Link these to use CMocka 10 | # CMOCKA_DEFINITIONS - Compiler switches required for using CMocka 11 | # 12 | #============================================================================= 13 | # Copyright (c) 2011-2012 Andreas Schneider 14 | # 15 | # Distributed under the OSI-approved BSD License (the "License"); 16 | # see accompanying file Copyright.txt for details. 17 | # 18 | # This software is distributed WITHOUT ANY WARRANTY; without even the 19 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 20 | # See the License for more information. 21 | #============================================================================= 22 | # 23 | 24 | find_path(CMOCKA_INCLUDE_DIR 25 | NAMES 26 | cmocka.h 27 | PATHS 28 | ${CMOCKA_ROOT_DIR}/include 29 | ) 30 | 31 | find_library(CMOCKA_LIBRARY 32 | NAMES 33 | cmocka 34 | PATHS 35 | ${CMOCKA_ROOT_DIR}/lib 36 | ) 37 | 38 | if (CMOCKA_LIBRARY) 39 | set(CMOCKA_LIBRARIES 40 | ${CMOCKA_LIBRARIES} 41 | ${CMOCKA_LIBRARY} 42 | ) 43 | endif (CMOCKA_LIBRARY) 44 | 45 | include(FindPackageHandleStandardArgs) 46 | find_package_handle_standard_args(CMocka DEFAULT_MSG CMOCKA_LIBRARIES CMOCKA_INCLUDE_DIR) 47 | 48 | # show the CMOCKA_INCLUDE_DIR and CMOCKA_LIBRARIES variables only in the advanced view 49 | mark_as_advanced(CMOCKA_INCLUDE_DIR CMOCKA_LIBRARIES) 50 | -------------------------------------------------------------------------------- /tests/test_mocks.c: -------------------------------------------------------------------------------- 1 | #include "test_common.h" 2 | #include 3 | #include "iota/bundle.h" 4 | #include "keccak/sha3.h" 5 | 6 | void throw_exception(const char *expression, const char *file, int line) 7 | { 8 | mock_assert(false, expression, file, line); 9 | } 10 | 11 | void ui_display_welcome() 12 | { 13 | } 14 | 15 | void ui_display_calc() 16 | { 17 | } 18 | 19 | void ui_display_recv() 20 | { 21 | } 22 | 23 | void ui_display_signing() 24 | { 25 | } 26 | 27 | void ui_display_address(const unsigned char *addr_bytes) 28 | { 29 | UNUSED(addr_bytes); 30 | } 31 | 32 | // forward declaration 33 | void user_sign(); 34 | 35 | void ui_sign_tx(BUNDLE_CTX *bundle_ctx) 36 | { 37 | UNUSED(bundle_ctx); 38 | 39 | // the user signs everything 40 | user_sign(); 41 | } 42 | 43 | void ui_restore() 44 | { 45 | } 46 | 47 | bool flash_is_init() 48 | { 49 | return true; 50 | } 51 | 52 | __attribute__((weak)) void derive_seed_bip32(const unsigned int *path, 53 | unsigned int pathLength, 54 | unsigned char *seed_bytes) 55 | { 56 | UNUSED(path); 57 | UNUSED(pathLength); 58 | UNUSED(seed_bytes); 59 | 60 | char msg[100]; 61 | snprintf(msg, 100, "%s should not be called", __FUNCTION__); 62 | mock_assert(false, msg, __FILE__, __LINE__); 63 | } 64 | 65 | __attribute__((weak)) void io_send(const void *ptr, unsigned int length, 66 | unsigned short sw) 67 | 68 | { 69 | UNUSED(ptr); 70 | UNUSED(length); 71 | UNUSED(sw); 72 | 73 | char msg[100]; 74 | snprintf(msg, 100, "%s should not be called", __FUNCTION__); 75 | mock_assert(false, msg, __FILE__, __LINE__); 76 | } 77 | -------------------------------------------------------------------------------- /src/iota/kerl.c: -------------------------------------------------------------------------------- 1 | #include "kerl.h" 2 | #include "conversion.h" 3 | #include "common.h" 4 | 5 | void kerl_initialize(cx_sha3_t *sha3) 6 | { 7 | cx_keccak_init(sha3, 384); 8 | } 9 | 10 | void kerl_reinitialize(cx_sha3_t *sha3, const unsigned char *state_bytes) 11 | { 12 | kerl_initialize(sha3); 13 | kerl_absorb_chunk(sha3, state_bytes); 14 | } 15 | 16 | void kerl_absorb_bytes(cx_sha3_t *sha3, const unsigned char *bytes, 17 | unsigned int len) 18 | { 19 | cx_hash((cx_hash_t *)sha3, 0, (unsigned char *)bytes, len, NULL); 20 | } 21 | 22 | void kerl_absorb_chunk(cx_sha3_t *sha3, const unsigned char *bytes) 23 | { 24 | kerl_absorb_bytes(sha3, bytes, CX_KECCAK384_SIZE); 25 | } 26 | 27 | void kerl_squeeze_final_chunk(cx_sha3_t *sha3, unsigned char *bytes_out) 28 | { 29 | cx_hash((cx_hash_t *)sha3, CX_LAST, bytes_out, 0, bytes_out); 30 | bytes_set_last_trit_zero(bytes_out); 31 | } 32 | 33 | void kerl_squeeze_chunk(cx_sha3_t *sha3, unsigned char *bytes_out) 34 | { 35 | unsigned char state_bytes[CX_KECCAK384_SIZE]; 36 | 37 | kerl_state_squeeze_chunk(sha3, state_bytes, bytes_out); 38 | kerl_reinitialize(sha3, state_bytes); 39 | } 40 | 41 | void kerl_squeeze_bytes(cx_sha3_t *sha3, unsigned char *bytes, unsigned int len) 42 | { 43 | // absorbing happens in 48 word bigint chunks 44 | for (unsigned int i = 0; i < (len / CX_KECCAK384_SIZE); i++) { 45 | kerl_squeeze_chunk(sha3, bytes + CX_KECCAK384_SIZE * i); 46 | } 47 | } 48 | 49 | static inline void flip_hash_bytes(unsigned char *bytes) 50 | { 51 | for (unsigned int i = 0; i < CX_KECCAK384_SIZE; i++) { 52 | bytes[i] = ~bytes[i]; 53 | } 54 | } 55 | 56 | void kerl_state_squeeze_chunk(cx_sha3_t *sha3, unsigned char *state_bytes, 57 | unsigned char *bytes_out) 58 | { 59 | cx_hash((cx_hash_t *)sha3, CX_LAST, state_bytes, 0, state_bytes); 60 | 61 | os_memcpy(bytes_out, state_bytes, CX_KECCAK384_SIZE); 62 | bytes_set_last_trit_zero(bytes_out); 63 | 64 | // flip bytes for multiple squeeze 65 | flip_hash_bytes(state_bytes); 66 | } 67 | -------------------------------------------------------------------------------- /src/iota/signing.h: -------------------------------------------------------------------------------- 1 | #ifndef SIGNING_H 2 | #define SIGNING_H 3 | 4 | #include "stdbool.h" 5 | #include "iota_types.h" 6 | 7 | // the number of chunks in one signature fragment 8 | // this can be changed to any multiple of 3 up to 27 9 | // prefer larger number when possible 10 | #define SIGNATURE_FRAGMENT_SIZE 27 11 | 12 | // the maximum number of chunks is SEC_LVL*27 13 | #define NUM_SIGNATURE_FRAGMENTS(s) (CEILING(s*27, SIGNATURE_FRAGMENT_SIZE)) 14 | 15 | typedef struct SIGNING_CTX { 16 | unsigned char state[48]; // state of the last Kerl squeeze 17 | 18 | // bitfield storing all the indices 19 | uint32_t fragment_index; // index of the next fragment 20 | uint32_t last_fragment; // final fragment 21 | 22 | tryte_t hash[81]; // bundle hash used for signing 23 | } SIGNING_CTX; 24 | 25 | /** @brief Initializes the signing context for one complete signature. 26 | * @param ctx the signing context used 27 | * @param seed_bytes seed in 48 byte big endian encoding 28 | * @param address_idx index of the address 29 | * @param security security level, either 1,2 or 3 30 | * @param normalized_hash bundle hash as a 81 elemet tryte array 31 | */ 32 | void signing_initialize(SIGNING_CTX *ctx, const unsigned char *seed_bytes, 33 | uint32_t address_idx, uint8_t security, 34 | const tryte_t *normalized_hash); 35 | 36 | /** @brief Computes the next signature fragment. 37 | * @param ctx the signing context used 38 | * @param signature_bytes target array for the fragment in 48 byte encoding 39 | * @return index of the just computed signature fragment 40 | */ 41 | unsigned int signing_next_fragment(SIGNING_CTX *ctx, 42 | unsigned char *signature_bytes); 43 | 44 | /** @brief Return wether there are remaining signatrue fragments 45 | * @param ctx the signing context used 46 | * @return true, if there is another fragment, false otherwise 47 | */ 48 | static inline bool signing_has_next_fragment(const SIGNING_CTX *ctx) 49 | { 50 | return ctx->fragment_index <= ctx->last_fragment; 51 | } 52 | 53 | #endif // SIGNING_H 54 | -------------------------------------------------------------------------------- /tests/api_tests.h: -------------------------------------------------------------------------------- 1 | #ifndef API_TESTS_H 2 | #define API_TESTS_H 3 | 4 | #include "test_vectors.h" 5 | 6 | #define BIP32_PATH \ 7 | { \ 8 | 0x8000002C, 0x8000107A, 0x80000000, 0x00000000, 0x00000000 \ 9 | } 10 | 11 | #define EXPECT_API_OK(INS, input) \ 12 | ({ \ 13 | expect_value(io_send, ptr, NULL); \ 14 | expect_value(io_send, length, 0); \ 15 | expect_value(io_send, sw, 0x9000); \ 16 | api_ ## INS((unsigned char *)&input, sizeof(input)); \ 17 | }) 18 | 19 | #define EXPECT_API_DATA_OK(INS, input, output) \ 20 | ({ \ 21 | expect_memory(io_send, ptr, &output, sizeof(output)); \ 22 | expect_value(io_send, length, sizeof(output)); \ 23 | expect_value(io_send, sw, 0x9000); \ 24 | api_ ## INS((unsigned char *)&input, sizeof(input)); \ 25 | }) 26 | 27 | #define EXPECT_API_EXCEPTION(INS, input) \ 28 | ({ \ 29 | expect_assert_failure(api_ ## INS((unsigned char *)&input, \ 30 | sizeof(input))); \ 31 | }) 32 | 33 | #define SEED_INIT(seed) \ 34 | ({ \ 35 | will_return (derive_seed_bip32, \ 36 | cast_ptr_to_largest_integral_type(seed)); \ 37 | }) 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /tests/transaction_file.c: -------------------------------------------------------------------------------- 1 | #include "transaction_file.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "test_common.h" 7 | #include "api.h" 8 | 9 | #define BUFFER_LEN 10000 10 | #define LAST_TX_INDEX 5 11 | #define NUM_INPUTS 2 12 | 13 | #define SCNs27 "[NOPQRSTUVWXYZ9ABCDEFGHIJKLM]" 14 | 15 | void test_for_each_bundle(const char *file_name, 16 | void (*test)(char *, TX_INPUT *, char *, 17 | char[][SIGNATURE_LENGTH])) 18 | { 19 | char path_name[BUFFER_LEN]; 20 | snprintf(path_name, sizeof(path_name), "%s/%s", TEST_FOLDER, file_name); 21 | 22 | FILE *file; 23 | if ((file = fopen(path_name, "r")) == NULL) { 24 | fail_msg("Could not open file."); 25 | } 26 | 27 | char line[BUFFER_LEN]; 28 | unsigned int line_num = 0; 29 | while (fgets(line, sizeof(line), file) != NULL) { 30 | 31 | if (++line_num == 1) { 32 | continue; 33 | } 34 | 35 | char seed[NUM_HASH_TRYTES]; 36 | TX_INPUT tx[LAST_TX_INDEX + 1]; 37 | 38 | int offset = 0; 39 | 40 | int scanned; 41 | sscanf(line, "%81" SCNs27 ",%n", seed, &scanned); 42 | offset += scanned; 43 | 44 | for (int i = 0; i <= LAST_TX_INDEX; i++) { 45 | sscanf(line + offset, 46 | "%81" SCNs27 ",%" SCNd64 ",%" SCNd64 ",%27" SCNs27 47 | ",%" SCNd64 ",%n", 48 | tx[i].address, &tx[i].address_idx, &tx[i].value, tx[i].tag, 49 | &tx[i].timestamp, &scanned); 50 | 51 | tx[i].current_index = i; 52 | tx[i].last_index = LAST_TX_INDEX; 53 | 54 | offset += scanned; 55 | } 56 | 57 | char bundle_hash[NUM_HASH_TRYTES]; 58 | sscanf(line + offset, "%81" SCNs27 ",%n", bundle_hash, &scanned); 59 | offset += scanned; 60 | 61 | char signatures[NUM_INPUTS][SIGNATURE_LENGTH]; 62 | for (int i = 0; i < NUM_INPUTS; i++) { 63 | sscanf(line + offset, "%4374" SCNs27 ",%n", signatures[i], 64 | &scanned); 65 | offset += scanned; 66 | } 67 | 68 | test(seed, tx, bundle_hash, signatures); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/iota/signing.c: -------------------------------------------------------------------------------- 1 | #include "signing.h" 2 | #include "common.h" 3 | #include "conversion.h" 4 | #include "kerl.h" 5 | 6 | static void initialize_state(const unsigned char *seed_bytes, 7 | uint32_t address_idx, unsigned char *state) 8 | { 9 | os_memcpy(state, seed_bytes, 48); 10 | bytes_add_u32_mem(state, address_idx); 11 | 12 | cx_sha3_t sha; 13 | kerl_initialize(&sha); 14 | kerl_absorb_chunk(&sha, state); 15 | kerl_squeeze_final_chunk(&sha, state); 16 | } 17 | 18 | void signing_initialize(SIGNING_CTX *ctx, const unsigned char *seed_bytes, 19 | uint32_t address_idx, uint8_t security, 20 | const tryte_t *normalized_hash) 21 | { 22 | os_memset(ctx, 0, sizeof(SIGNING_CTX)); 23 | 24 | initialize_state(seed_bytes, address_idx, ctx->state); 25 | ctx->last_fragment = NUM_SIGNATURE_FRAGMENTS(security) - 1; 26 | 27 | os_memcpy(ctx->hash, normalized_hash, 81); 28 | } 29 | 30 | static void generate_signature_fragment(unsigned char *state, 31 | const tryte_t *hash_fragment, 32 | unsigned char *signature_bytes) 33 | { 34 | cx_sha3_t sha; 35 | kerl_reinitialize(&sha, state); 36 | 37 | for (unsigned int j = 0; j < SIGNATURE_FRAGMENT_SIZE; j++) { 38 | unsigned char *signature_f = signature_bytes + j * NUM_HASH_BYTES; 39 | 40 | // the output of the squeeze is exactly the private key 41 | kerl_state_squeeze_chunk(&sha, state, signature_f); 42 | 43 | for (unsigned int k = MAX_TRYTE_VALUE - hash_fragment[j]; k-- > 0;) { 44 | kerl_initialize(&sha); 45 | kerl_absorb_chunk(&sha, signature_f); 46 | kerl_squeeze_final_chunk(&sha, signature_f); 47 | } 48 | 49 | // if we are not the the final iteration reinitialize to get next key_f 50 | if (j < SIGNATURE_FRAGMENT_SIZE - 1) { 51 | kerl_reinitialize(&sha, state); 52 | } 53 | } 54 | } 55 | 56 | unsigned int signing_next_fragment(SIGNING_CTX *ctx, 57 | unsigned char *signature_bytes) 58 | { 59 | if (!signing_has_next_fragment(ctx)) { 60 | THROW(INVALID_STATE); 61 | } 62 | 63 | generate_signature_fragment( 64 | ctx->state, ctx->hash + ctx->fragment_index * SIGNATURE_FRAGMENT_SIZE, 65 | signature_bytes); 66 | 67 | return ctx->fragment_index++; 68 | } 69 | -------------------------------------------------------------------------------- /src/keccak/options.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2014 Pavol Rusnak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 18 | * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 | * OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef __OPTIONS_H__ 24 | #define __OPTIONS_H__ 25 | 26 | // use precomputed Curve Points (some scalar multiples of curve base point G) 27 | #ifndef USE_PRECOMPUTED_CP 28 | #define USE_PRECOMPUTED_CP 1 29 | #endif 30 | 31 | // use fast inverse method 32 | #ifndef USE_INVERSE_FAST 33 | #define USE_INVERSE_FAST 1 34 | #endif 35 | 36 | // support for printing bignum256 structures via printf 37 | #ifndef USE_BN_PRINT 38 | #define USE_BN_PRINT 0 39 | #endif 40 | 41 | // use deterministic signatures 42 | #ifndef USE_RFC6979 43 | #define USE_RFC6979 1 44 | #endif 45 | 46 | // implement BIP32 caching 47 | #ifndef USE_BIP32_CACHE 48 | #define USE_BIP32_CACHE 1 49 | #define BIP32_CACHE_SIZE 10 50 | #define BIP32_CACHE_MAXDEPTH 8 51 | #endif 52 | 53 | // implement BIP39 caching 54 | #ifndef USE_BIP39_CACHE 55 | #define USE_BIP39_CACHE 1 56 | #define BIP39_CACHE_SIZE 4 57 | #endif 58 | 59 | // support Ethereum operations 60 | #ifndef USE_ETHEREUM 61 | #define USE_ETHEREUM 0 62 | #endif 63 | 64 | // support Graphene operations (STEEM, BitShares) 65 | #ifndef USE_GRAPHENE 66 | #define USE_GRAPHENE 0 67 | #endif 68 | 69 | // support NEM operations 70 | #ifndef USE_NEM 71 | #define USE_NEM 0 72 | #endif 73 | 74 | // support Keccak hashing 75 | #ifndef USE_KECCAK 76 | #define USE_KECCAK 1 77 | #endif 78 | 79 | // add way how to mark confidential data 80 | #ifndef CONFIDENTIAL 81 | #define CONFIDENTIAL 82 | #endif 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /src/iota/kerl.h: -------------------------------------------------------------------------------- 1 | /** @file kerl.h 2 | * @brief Kerl functionality on raw byte data. 3 | */ 4 | 5 | #ifndef KERL_H 6 | #define KERL_H 7 | 8 | #include "common.h" 9 | 10 | /** @brief Initializes the context for Kerl. 11 | * @param sha3 the SHA context used. 12 | */ 13 | void kerl_initialize(cx_sha3_t *sha3); 14 | 15 | /** @brief Reinitialize the context with the given Kerl state. 16 | * A reinitialized context can then be used to squeeze further chunks. 17 | * @param sha3 the SHA context used for hashing 18 | * @param state_bytes byte array containing the 48 byte Kerl state 19 | */ 20 | void kerl_reinitialize(cx_sha3_t *sha3, const unsigned char *state_bytes); 21 | 22 | /** @brief Absorb exactly one chunk of 48 bytes. 23 | * @param sha3 the SHA context used for hashing 24 | * @param bytes bytes to absorb. 25 | */ 26 | void kerl_absorb_chunk(cx_sha3_t *sha3, const unsigned char* bytes); 27 | 28 | /** @brief Absorb arbitrary number of bytes. 29 | * @param sha3 the SHA context used for hashing 30 | * @param bytes bytes to absorb. 31 | */ 32 | void kerl_absorb_bytes(cx_sha3_t *sha3, const unsigned char* bytes, unsigned int len); 33 | 34 | /** @brief Squeeze exactly one chunk of 48 bytes. 35 | * This function automatically reinitializes kerl in the corresponding state. 36 | * @param sha3 the SHA context used for hashing 37 | * @param bytes result byte array 38 | */ 39 | void kerl_squeeze_chunk(cx_sha3_t *sha3, unsigned char* bytes); 40 | 41 | /** @brief Squeeze exactly one chunk of 48 bytes without reinitializing the 42 | * hash context to allow for multiple squeeze. 43 | * This funtion should be called, if no further squeeze are performed on this 44 | * context, as it avoid unnecessary reinitializations. 45 | * @param sha3 the SHA context used for hashing 46 | * @param bytes result byte array 47 | */ 48 | void kerl_squeeze_final_chunk(cx_sha3_t *sha3, unsigned char *bytes_out); 49 | 50 | /** @brief Squeeze multiple chunks of 48 byte data. 51 | * @param sha3 the SHA context used for hashing 52 | * @param bytes result byte array 53 | * @param len number of bytes to squeeze. 54 | */ 55 | void kerl_squeeze_bytes(cx_sha3_t *sha3, unsigned char* bytes, unsigned int len); 56 | 57 | /** @brief Squeeze exactly one chunk of 48 bytes also returning the kerl state. 58 | * The hash returned is identical to kerl_squeeze_chunk(). 59 | * The 48 byte kerl state can then be used in kerl_initialize_squeeze() to put any SHA context in the state. 60 | * @param sha3 the SHA context used for hashing 61 | * @param state_bytes target byte array to store the 48 byte state 62 | * @param bytes result byte array 63 | */ 64 | void kerl_state_squeeze_chunk(cx_sha3_t *sha3, unsigned char *state_bytes, unsigned char *bytes); 65 | 66 | #endif // KERL_H 67 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | project(iota-ledger-test C) 4 | set(CMAKE_C_STANDARD 99) 5 | set(CMAKE_C_STANDARD_REQUIRED TRUE) 6 | 7 | enable_testing() 8 | 9 | set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) 10 | 11 | find_package(CMocka REQUIRED) 12 | include_directories(${CMOCKA_INCLUDE_DIR}) 13 | 14 | include_directories( 15 | "../src" 16 | "../src/iota" 17 | "../src/keccak" 18 | ) 19 | 20 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wshadow -Wcast-align") 21 | 22 | # enable compilation on host 23 | add_definitions( 24 | -DNO_BOLOS 25 | -DKERL_TEST_FOLDER="${CMAKE_SOURCE_DIR}/kerl-spec/test_vectors" 26 | -DTEST_FOLDER="${CMAKE_SOURCE_DIR}/test_vectors" 27 | ) 28 | 29 | add_library(iota-ledger SHARED 30 | "../src/iota/addresses.c" 31 | "../src/iota/bundle.c" 32 | "../src/iota/conversion.c" 33 | "../src/iota/kerl.c" 34 | "../src/iota/signing.c" 35 | "../src/keccak/sha3.c" 36 | "../src/api.c" 37 | "../src/aux.c" 38 | "test_mocks.c" 39 | ) 40 | 41 | add_executable(conversion_test conversion_test.c) 42 | target_link_libraries(conversion_test ${CMOCKA_LIBRARIES} iota-ledger) 43 | add_test(conversion_test ${CMAKE_CURRENT_BINARY_DIR}/conversion_test) 44 | 45 | add_executable(kerl_test kerl_test.c hash_file.c) 46 | target_link_libraries(kerl_test ${CMOCKA_LIBRARIES} iota-ledger) 47 | add_test(kerl_test ${CMAKE_CURRENT_BINARY_DIR}/kerl_test) 48 | 49 | add_executable(address_test address_test.c hash_file.c) 50 | target_link_libraries(address_test ${CMOCKA_LIBRARIES} iota-ledger) 51 | add_test(address_test ${CMAKE_CURRENT_BINARY_DIR}/address_test) 52 | 53 | add_executable(bundle_test bundle_test.c) 54 | target_link_libraries(bundle_test ${CMOCKA_LIBRARIES} iota-ledger) 55 | add_test(bundle_test ${CMAKE_CURRENT_BINARY_DIR}/bundle_test) 56 | 57 | add_executable(signing_test signing_test.c) 58 | target_link_libraries(signing_test ${CMOCKA_LIBRARIES} iota-ledger) 59 | add_test(signing_test ${CMAKE_CURRENT_BINARY_DIR}/signing_test) 60 | 61 | add_executable(set_seed_test set_seed_test.c) 62 | target_link_libraries(set_seed_test ${CMOCKA_LIBRARIES} iota-ledger) 63 | add_test(set_seed_test ${CMAKE_CURRENT_BINARY_DIR}/set_seed_test) 64 | 65 | add_executable(pubkey_test pubkey_test.c) 66 | target_link_libraries(pubkey_test ${CMOCKA_LIBRARIES} iota-ledger) 67 | add_test(pubkey_test ${CMAKE_CURRENT_BINARY_DIR}/pubkey_test) 68 | 69 | add_executable(tx_test tx_test.c transaction_file.c) 70 | target_link_libraries(tx_test ${CMOCKA_LIBRARIES} iota-ledger) 71 | add_test(tx_test ${CMAKE_CURRENT_BINARY_DIR}/tx_test) 72 | 73 | add_executable(sign_test sign_test.c transaction_file.c) 74 | target_link_libraries(sign_test ${CMOCKA_LIBRARIES} iota-ledger) 75 | add_test(sign_test ${CMAKE_CURRENT_BINARY_DIR}/sign_test) 76 | -------------------------------------------------------------------------------- /tests/set_seed_test.c: -------------------------------------------------------------------------------- 1 | #include "test_common.h" 2 | #include 3 | #include "api_tests.h" 4 | #include "api.h" 5 | #include "iota/conversion.h" 6 | 7 | void derive_seed_bip32(const unsigned int *path, unsigned int pathLength, 8 | unsigned char *seed_bytes) 9 | { 10 | check_expected(path); 11 | check_expected(pathLength); 12 | 13 | chars_to_bytes(mock_ptr_type(char *), seed_bytes, NUM_HASH_TRYTES); 14 | } 15 | 16 | void io_send(const void *ptr, unsigned int length, unsigned short sw) 17 | { 18 | check_expected(ptr); 19 | check_expected(length); 20 | check_expected(sw); 21 | } 22 | 23 | static void test_valid_security_levels(void **state) 24 | { 25 | UNUSED(state); 26 | 27 | static const unsigned int path[] = BIP32_PATH; 28 | 29 | for (unsigned int security = 1; security <= 3; security++) { 30 | 31 | expect_memory(derive_seed_bip32, path, path, sizeof(path)); 32 | expect_value(derive_seed_bip32, pathLength, 33 | sizeof(path) / sizeof(path[0])); 34 | 35 | will_return(derive_seed_bip32, 36 | cast_ptr_to_largest_integral_type(PETER_VECTOR.seed)); 37 | 38 | SET_SEED_INPUT input = {BIP32_PATH, security}; 39 | 40 | api_initialize(); 41 | EXPECT_API_OK(set_seed, input); 42 | } 43 | } 44 | 45 | static void test_security_level_zero(void **state) 46 | { 47 | UNUSED(state); 48 | 49 | SET_SEED_INPUT input = {BIP32_PATH, 0}; 50 | 51 | api_initialize(); 52 | EXPECT_API_EXCEPTION(set_seed, input); 53 | } 54 | 55 | static void test_security_level_four(void **state) 56 | { 57 | UNUSED(state); 58 | 59 | SET_SEED_INPUT input = {BIP32_PATH, 4}; 60 | 61 | api_initialize(); 62 | EXPECT_API_EXCEPTION(set_seed, input); 63 | } 64 | 65 | static void test_invalid_negative_path(void **state) 66 | { 67 | UNUSED(state); 68 | 69 | int64_t path[] = BIP32_PATH; 70 | path[4] = -1; 71 | 72 | SET_SEED_INPUT input; 73 | input.security = 1; 74 | memcpy(input.bip44_path, path, sizeof(path)); 75 | 76 | api_initialize(); 77 | EXPECT_API_EXCEPTION(set_seed, input); 78 | } 79 | 80 | static void test_path_overflow(void **state) 81 | { 82 | UNUSED(state); 83 | 84 | int64_t path[] = BIP32_PATH; 85 | path[4] = UINT32_MAX + INT64_C(1); 86 | 87 | SET_SEED_INPUT input; 88 | input.security = 1; 89 | memcpy(input.bip44_path, path, sizeof(path)); 90 | 91 | api_initialize(); 92 | EXPECT_API_EXCEPTION(set_seed, input); 93 | } 94 | 95 | int main(void) 96 | { 97 | const struct CMUnitTest tests[] = { 98 | cmocka_unit_test(test_valid_security_levels), 99 | cmocka_unit_test(test_security_level_zero), 100 | cmocka_unit_test(test_security_level_four), 101 | cmocka_unit_test(test_invalid_negative_path), 102 | cmocka_unit_test(test_path_overflow)}; 103 | 104 | return cmocka_run_group_tests(tests, NULL, NULL); 105 | } 106 | -------------------------------------------------------------------------------- /src/keccak/sha3.h: -------------------------------------------------------------------------------- 1 | /* sha3.h - an implementation of Secure Hash Algorithm 3 (Keccak). 2 | * based on the 3 | * The Keccak SHA-3 submission. Submission to NIST (Round 3), 2011 4 | * by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche 5 | * 6 | * Copyright: 2013 Aleksey Kravchenko 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a 9 | * copy of this software and associated documentation files (the "Software"), 10 | * to deal in the Software without restriction, including without limitation 11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 | * and/or sell copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so. 14 | * 15 | * This program is distributed in the hope that it will be useful, but 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 17 | * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! 18 | */ 19 | 20 | #ifndef __SHA3_H__ 21 | #define __SHA3_H__ 22 | 23 | #include 24 | #include "options.h" 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | #define sha3_224_hash_size 28 31 | #define sha3_256_hash_size 32 32 | #define sha3_384_hash_size 48 33 | #define sha3_512_hash_size 64 34 | #define sha3_max_permutation_size 25 35 | #define sha3_max_rate_in_qwords 24 36 | 37 | #define SHA3_224_BLOCK_LENGTH 144 38 | #define SHA3_256_BLOCK_LENGTH 136 39 | #define SHA3_384_BLOCK_LENGTH 104 40 | #define SHA3_512_BLOCK_LENGTH 72 41 | 42 | #define SHA3_224_DIGEST_LENGTH sha3_224_hash_size 43 | #define SHA3_256_DIGEST_LENGTH sha3_256_hash_size 44 | #define SHA3_384_DIGEST_LENGTH sha3_384_hash_size 45 | #define SHA3_512_DIGEST_LENGTH sha3_512_hash_size 46 | 47 | /** 48 | * SHA3 Algorithm context. 49 | */ 50 | typedef struct SHA3_CTX 51 | { 52 | /* 1600 bits algorithm hashing state */ 53 | uint64_t hash[sha3_max_permutation_size]; 54 | /* 1536-bit buffer for leftovers */ 55 | uint64_t message[sha3_max_rate_in_qwords]; 56 | /* count of bytes in the message[] buffer */ 57 | unsigned rest; 58 | /* size of a message block processed at once */ 59 | unsigned block_size; 60 | } SHA3_CTX; 61 | 62 | /* methods for calculating the hash function */ 63 | 64 | void sha3_224_Init(SHA3_CTX *ctx); 65 | void sha3_256_Init(SHA3_CTX *ctx); 66 | void sha3_384_Init(SHA3_CTX *ctx); 67 | void sha3_512_Init(SHA3_CTX *ctx); 68 | void sha3_Update(SHA3_CTX *ctx, const unsigned char* msg, size_t size); 69 | void sha3_Final(SHA3_CTX *ctx, unsigned char* result); 70 | 71 | #if USE_KECCAK 72 | #define keccak_224_Init sha3_224_Init 73 | #define keccak_256_Init sha3_256_Init 74 | #define keccak_384_Init sha3_384_Init 75 | #define keccak_512_Init sha3_512_Init 76 | #define keccak_Update sha3_Update 77 | void keccak_Final(SHA3_CTX *ctx, unsigned char* result); 78 | void keccak_256(const unsigned char* data, size_t len, unsigned char* digest); 79 | void keccak_512(const unsigned char* data, size_t len, unsigned char* digest); 80 | #endif 81 | 82 | void sha3_256(const unsigned char* data, size_t len, unsigned char* digest); 83 | void sha3_512(const unsigned char* data, size_t len, unsigned char* digest); 84 | 85 | #ifdef __cplusplus 86 | } /* extern "C" */ 87 | #endif /* __cplusplus */ 88 | 89 | #endif /* __SHA3_H__ */ 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Notice 2 | 3 | Not under development, kind of deprecated. 4 | I decided to split this wallet into three parts due to seperation of concerns. 5 | 6 | IOTA C library: The library contains the basic operation to build an iota 7 | transaction bundle. POSIX, C99, thread-safe, no malloc. To make sure that 8 | it runs on any device in any operation system. 9 | 10 | Wallet header files: Defines the universal header files which needs to get 11 | implemented. Higher level fund managment included. 12 | 13 | Wallet implementation: An operation system, database etc. dependend implementation 14 | of an iota wallet. 15 | 16 | The goal is that every application which includes the wallet header can run on any 17 | operation system and any implementation. 18 | 19 | IOTA C library: 20 | 21 | https://github.com/embedded-iota/iota-c-library 22 | 23 | RIOS OS (IOTA C library): 24 | 25 | https://github.com/Citrullin/RIOT/tree/master/pkg/iota-library 26 | 27 | https://github.com/Citrullin/RIOT/tree/master/examples/iota_transaction_pthread 28 | 29 | 30 | # IOTA C Light Wallet 31 | 32 | A big thanks to the people of the IOTA implementation of the Ledger Nano S. 33 | They create an amazing low footprint C light wallet implementation for the Nano S. 34 | We use this as base for the generic embedded C light wallet implementation. 35 | 36 | ## A notice to trytes: 37 | 38 | Due to confusion of several people, a short explanation about the conversions. 39 | The API for the conversion is available in conversion.c and conversion.h. 40 | 41 | In this context the following words have the following meaning: 42 | 43 | **Chars:** Means the char representation of an Tryte. (ASCII character) A - Z and 9. (base-27 encoded ternary number) 44 | **Bytes:** Means a byte representation of trytes, optimized for low storage and memory usage. 45 | **Trits:** Means a int8_t array representation of trits. 46 | **Trytes:** Means a int8_t array representation of trytes. 47 | 48 | ### Conversion space ratio 49 | 50 | One transaction = 2673 trytes = ((2673/81) * 48) Bytes = 2673*3 Trits = 2673 chars 51 | 52 | 53 | ## Usage: 54 | ### Generation of addresses 55 | 56 | 57 | ``` 58 | unsigned char address[81]; 59 | char seedChars[] = "INSERT_SEED_HERE"; 60 | unsigned char seedBytes[48]; 61 | chars_to_bytes(seedChars, seedBytes, 81); 62 | get_public_addr(seedBytes, 0, 2, address); 63 | 64 | char charAddress[81]; 65 | bytes_to_chars(address, charAddress, 48); 66 | ``` 67 | 68 | ### Generation of transactions and bundles 69 | 70 | ``` 71 | char *seed = "YOURSEEDSTRING"; // 81 ternary characters 72 | char address_one[82] = {0}; 73 | char address_two[82] = {0}; 74 | char *tag = "ANTAG"; //27 ternary characters 75 | 76 | //Define the output array, where the coins must go to. 77 | TX_OUTPUT output_txs[] = 78 | {{.address = "ANADDRESS", .value = 10000, .message = "", .tag = ""}}; //ANADDRESS => 81 ternary characters 79 | 80 | 81 | //Define the input array. Where the coins come from 82 | TX_INPUT input_txs[] = {{.key_index = 4, .balance = 10000}}; 83 | 84 | //Define the transaction chars array. The char trytes will saved in this array. (base-27 encoded) 85 | char transaction_chars[10][2673]; 86 | //Get all raw transaction trytes. Will saved in transaction_chars 87 | prepare_transfers(seed, 2, output_txs, 1, input_txs, 1, transaction_chars); 88 | 89 | ``` 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /demos/python/demo.py: -------------------------------------------------------------------------------- 1 | from ledgerblue.comm import getDongle 2 | from ledgerblue.commException import CommException 3 | from struct import Struct 4 | import time 5 | import sys 6 | 7 | BIP44_PATH = [0x8000002C, 8 | 0x8000107A, 9 | 0x80000000, 10 | 0x00000000, 11 | 0x00000000] 12 | SECURITY_LEVEL = 2 13 | 14 | DEST_ADDRESS = b"ADR" * 27 15 | SRC_INDEX = 1 16 | TIMESTAMP = 99999 17 | 18 | # APDU instructions 19 | INS_SET_SEED = 0x01 20 | INS_PUBKEY = 0x02 21 | INS_TX = 0x03 22 | INS_SIGN = 0x04 23 | INS_DISP_ADDR = 0x05 24 | 25 | 26 | def apdu_command(ins, data, p1=0, p2=0): 27 | b = bytes(data) 28 | 29 | command = bytearray() 30 | command.append(0x80) # Instruction class (1) 31 | command.append(ins) # Instruction code (1) 32 | command.extend([p1, p2]) # Instruction parameters (2) 33 | command.append(len(b)) # length of data (1) 34 | command.extend(b) # Command data 35 | command.append(0) 36 | 37 | return command 38 | 39 | 40 | def pack_set_seed_input(bip44_path): 41 | struct = Struct("<5qq") 42 | return struct.pack(bip44_path[0], bip44_path[1], bip44_path[2], bip44_path[3], bip44_path[4], SECURITY_LEVEL) 43 | 44 | 45 | def pack_pub_key_input(address_idx): 46 | struct = Struct(" 5 | #include 6 | #include 7 | #include "../keccak/sha3.h" 8 | 9 | /* ----------------------------------------------------------------------- */ 10 | /* - DEFINES AND MACROS - */ 11 | /* ----------------------------------------------------------------------- */ 12 | 13 | // no catching supported, just abort while printing the code name to stderr 14 | #define THROW(x) \ 15 | ({ fprintf(stderr, "EXCEPTION: " #x "\n"); \ 16 | abort(); }) 17 | 18 | #define UNUSED(x) (void)(x) 19 | 20 | #define MIN(a,b) \ 21 | ({ __typeof__ (a)_a = (a); \ 22 | __typeof__ (b)_b = (b); \ 23 | _a < _b ? _a : _b; }) 24 | #define MAX(a,b) \ 25 | ({ __typeof__ (a)_a = (a); \ 26 | __typeof__ (b)_b = (b); \ 27 | _a > _b ? _a : _b; }) 28 | 29 | /* ----------------------------------------------------------------------- */ 30 | /* - OS ALTERNATIVES - */ 31 | /* ----------------------------------------------------------------------- */ 32 | 33 | #define os_swap_u32 __builtin_bswap32 34 | 35 | #define os_memmove memmove 36 | #define os_memcpy memcpy 37 | 38 | #define os_memset memset 39 | 40 | /* ----------------------------------------------------------------------- */ 41 | /* - CRYPTO FUNCTIONS - */ 42 | /* ----------------------------------------------------------------------- */ 43 | 44 | #define CX_LAST (1 << 0) 45 | 46 | typedef SHA3_CTX cx_sha3_t; 47 | typedef SHA3_CTX cx_hash_t; 48 | 49 | static inline void cx_keccak_init(SHA3_CTX* hash, int size) { 50 | UNUSED(size); 51 | 52 | keccak_384_Init(hash); 53 | } 54 | 55 | static inline void cx_hash(SHA3_CTX* hash, int mode, const unsigned char *in, 56 | unsigned int len, unsigned char *out) { 57 | 58 | if (mode != CX_LAST) { 59 | // if CX_LAST is not set, add input data to add to current hash 60 | keccak_Update(hash, in, len); 61 | } else if (len == 0) { 62 | // if no input data given, compute and copy the hash 63 | keccak_Final(hash, out); 64 | } else { 65 | // if CX_LAST is set, compute hash for input 66 | keccak_Update(hash, in, len); 67 | keccak_Final(hash, out); 68 | } 69 | } 70 | 71 | /* ----------------------------------------------------------------------- */ 72 | /* - IO - */ 73 | /* ----------------------------------------------------------------------- */ 74 | 75 | #define IO_ASYNCH_REPLY 1 76 | 77 | /* ----------------------------------------------------------------------- */ 78 | /* - COMMON - */ 79 | /* ----------------------------------------------------------------------- */ 80 | 81 | #define CX_KECCAK384_SIZE 48 82 | 83 | #define CEILING(x,y) (((x) + (y) - 1) / (y)) 84 | 85 | #define ASSIGN(dest, src) \ 86 | ({ \ 87 | typeof(src)_x = (src); \ 88 | typeof(dest)_y = _x; \ 89 | (_x == _y && ((_x < 1) == (_y < 1)) ? (void)((dest) = _y), \ 90 | 1 : 0); \ 91 | }) 92 | 93 | #define IN_RANGE(x, min, max) \ 94 | ({ \ 95 | typeof(x)_x = (x); \ 96 | (_x >= (min) && _x <= (max)); \ 97 | }) 98 | 99 | #endif // COMMON_H 100 | -------------------------------------------------------------------------------- /src/iota/addresses.c: -------------------------------------------------------------------------------- 1 | #include "addresses.h" 2 | #include "common.h" 3 | #include "conversion.h" 4 | #include "kerl.h" 5 | #include 6 | 7 | #define CHECKSUM_CHARS 9 8 | 9 | static void digest_single_chunk(unsigned char *key_fragment, 10 | cx_sha3_t *digest_sha3, cx_sha3_t *round_sha3) 11 | { 12 | for (int k = 0; k < 26; k++) { 13 | kerl_initialize(round_sha3); 14 | kerl_absorb_chunk(round_sha3, key_fragment); 15 | kerl_squeeze_final_chunk(round_sha3, key_fragment); 16 | } 17 | 18 | // absorb buffer directly to avoid storing the digest fragment 19 | kerl_absorb_chunk(digest_sha3, key_fragment); 20 | } 21 | 22 | // initialize the sha3 instance for generating private key 23 | static void init_shas(const unsigned char *seed_bytes, uint32_t idx, 24 | cx_sha3_t *key_sha, cx_sha3_t *digest_sha) 25 | { 26 | // use temp bigint so seed not destroyed 27 | unsigned char bytes[NUM_HASH_BYTES]; 28 | os_memcpy(bytes, seed_bytes, sizeof(bytes)); 29 | 30 | bytes_add_u32_mem(bytes, idx); 31 | 32 | kerl_initialize(key_sha); 33 | kerl_absorb_chunk(key_sha, bytes); 34 | kerl_squeeze_final_chunk(key_sha, bytes); 35 | 36 | kerl_initialize(key_sha); 37 | kerl_absorb_chunk(key_sha, bytes); 38 | 39 | kerl_initialize(digest_sha); 40 | } 41 | 42 | // generate public address in byte format 43 | void get_public_addr(const unsigned char *seed_bytes, uint32_t idx, 44 | unsigned int security, unsigned char *address_bytes) 45 | { 46 | if (!IN_RANGE(security, MIN_SECURITY_LEVEL, MAX_SECURITY_LEVEL)) { 47 | THROW(INVALID_PARAMETER); 48 | } 49 | 50 | // sha size is 424 bytes 51 | cx_sha3_t key_sha, digest_sha; 52 | 53 | // init private key sha, digest sha 54 | init_shas(seed_bytes, idx, &key_sha, &digest_sha); 55 | 56 | // buffer for the digests of each security level 57 | unsigned char digest[NUM_HASH_BYTES * security]; 58 | 59 | // only store a single fragment of the private key at a time 60 | // use last chunk of buffer, as this is only used after the key is generated 61 | unsigned char *key_f = digest + NUM_HASH_BYTES * (security - 1); 62 | 63 | for (uint8_t i = 0; i < security; i++) { 64 | for (uint8_t j = 0; j < 27; j++) { 65 | // use address output array as a temp Kerl state storage 66 | unsigned char *state = address_bytes; 67 | 68 | // the state takes only 48bytes and allows us to reuse key_sha 69 | kerl_state_squeeze_chunk(&key_sha, state, key_f); 70 | // re-use key_sha as round_sha 71 | digest_single_chunk(key_f, &digest_sha, &key_sha); 72 | 73 | // as key_sha has been tainted, reinitialize with the saved state 74 | kerl_reinitialize(&key_sha, state); 75 | } 76 | kerl_squeeze_final_chunk(&digest_sha, digest + NUM_HASH_BYTES * i); 77 | 78 | // reset digest sha for next digest 79 | kerl_initialize(&digest_sha); 80 | } 81 | 82 | // absorb the digest for each security 83 | kerl_absorb_bytes(&digest_sha, digest, NUM_HASH_BYTES * security); 84 | 85 | // one final squeeze for address 86 | kerl_squeeze_final_chunk(&digest_sha, address_bytes); 87 | } 88 | 89 | // get 9 character checksum of NUM_HASH_TRYTES character address 90 | void get_address_with_checksum(const unsigned char *address_bytes, 91 | char *full_address) 92 | { 93 | cx_sha3_t sha; 94 | kerl_initialize(&sha); 95 | 96 | unsigned char checksum_bytes[NUM_HASH_BYTES]; 97 | kerl_absorb_chunk(&sha, address_bytes); 98 | kerl_squeeze_final_chunk(&sha, checksum_bytes); 99 | 100 | char full_checksum[NUM_HASH_TRYTES]; 101 | bytes_to_chars(checksum_bytes, full_checksum, NUM_HASH_BYTES); 102 | 103 | bytes_to_chars(address_bytes, full_address, NUM_HASH_BYTES); 104 | 105 | os_memcpy(full_address + NUM_HASH_TRYTES, 106 | full_checksum + NUM_HASH_TRYTES - CHECKSUM_CHARS, CHECKSUM_CHARS); 107 | } 108 | -------------------------------------------------------------------------------- /src/iota/bundle.h: -------------------------------------------------------------------------------- 1 | #ifndef BUNDLE_H 2 | #define BUNDLE_H 3 | 4 | #include 5 | #include "iota_types.h" 6 | 7 | 8 | #define MAX_BUNDLE_INDEX_SZ 8 9 | 10 | typedef struct BUNDLE_CTX { 11 | // bundle_bytes holds all of the bundle information in byte encoding 12 | unsigned char bytes[MAX_BUNDLE_INDEX_SZ * 96]; 13 | 14 | uint32_t current_index; 15 | uint32_t last_index; 16 | 17 | int64_t values[MAX_BUNDLE_INDEX_SZ]; 18 | uint32_t indices[MAX_BUNDLE_INDEX_SZ]; 19 | 20 | unsigned char hash[48]; // bundle hash, when finalized 21 | } BUNDLE_CTX; 22 | 23 | /** @brief Initializes the bundle context for a fixed number of transactions. 24 | * @param ctx the bundle context used 25 | * @param last_index index of the last transaction in the bundle. Must be at 26 | * least 1 as at least an output and an input transaction is required 27 | */ 28 | void bundle_initialize(BUNDLE_CTX *ctx, uint32_t last_index); 29 | 30 | /** @brief Sets the address for the current output transaction. 31 | * The address must be set befor calling bundle_add_tx(). 32 | * @param ctx the bundle context used 33 | * @param address address in base-27 encoding 34 | */ 35 | void bundle_set_external_address(BUNDLE_CTX *ctx, const char *address); 36 | 37 | /** @brief Sets the address for the current input transaction. 38 | * The address must be set befor calling bundle_add_tx(). The index must match 39 | * the address, this is verified in bundle_validating_finalize(). 40 | * @param ctx the bundle context used 41 | * @param address address in base-27 encoding 42 | * @param index address index 43 | */ 44 | void bundle_set_internal_address(BUNDLE_CTX *ctx, const char *address, 45 | uint32_t index); 46 | 47 | /** @brief Sets the address for the current transaction. 48 | * The address must be set befor calling bundle_add_tx(). 49 | * @param ctx the bundle context used 50 | * @param address address in 48-byte big endian encoding 51 | */ 52 | void bundle_set_address_bytes(BUNDLE_CTX *ctx, const unsigned char *addresses); 53 | 54 | /** @brief Sets the content for the current transaction, finalizing it. 55 | * This will increment the current transaction, so that 56 | * bundle_set_address_chars(), bundle_set_address_bytes() can be called for 57 | * the next transaction. 58 | * @param ctx the bundle context used 59 | * @param value transaction signed value 60 | * @param tag transaction tag in base-27 encoding must be exactly 27 char long 61 | * @param timestamp transaction timestamp 62 | * @return index of the just finalized transaction. 63 | */ 64 | uint32_t bundle_add_tx(BUNDLE_CTX *ctx, int64_t value, const char *tag, 65 | uint32_t timestamp); 66 | 67 | /** @brief Finalizes the bundle by computing the valid bundle hash. 68 | * @param ctx the bundle context used. 69 | * @return tag increment of the first transaction that was necessary to 70 | * generate a valid bundle 71 | */ 72 | unsigned int bundle_finalize(BUNDLE_CTX *ctx); 73 | 74 | /** @brief Finalizes the bundle, if it has a valid bundle hash. 75 | * A bundle is valid, if a) values sum up to 0 b) the index of each input 76 | * transaction matches the provided address c) the normalized bundle hash does 77 | * not contain 'M'. 78 | * @param ctx the bundle context used. 79 | * @param change_index the index of the change transaction 80 | * @param seed_bytes seed used for the addresses 81 | * @param security security level used for the addresses 82 | * @return true if the bundle is valid, false otherwise 83 | */ 84 | bool bundle_validating_finalize(BUNDLE_CTX *ctx, uint32_t change_index, 85 | const unsigned char *seed_bytes, 86 | unsigned int security); 87 | 88 | /** @brief Returns the (not normalized) hash of the finalized bundle. 89 | * @param ctx the bundle context used 90 | */ 91 | const unsigned char* bundle_get_hash(const BUNDLE_CTX *ctx); 92 | 93 | /** @brief Returns a pointer to the address of the given transaction in 94 | * 48-byte representation. 95 | * This can only be called for an already finalized transaction index. 96 | * @param ctx the bundle context used 97 | * @param tx_index transaction index 98 | */ 99 | const unsigned char *bundle_get_address_bytes(const BUNDLE_CTX *ctx, 100 | uint32_t tx_index); 101 | 102 | /** @brief Computes the normalized hash. 103 | * @param ctx the bundle context used 104 | * @param hash_trytes target 81-tryte array for the normalized hash 105 | */ 106 | void bundle_get_normalized_hash(const BUNDLE_CTX *ctx, tryte_t *hash_trytes); 107 | 108 | /** @brief Returns whether there are still transactions missing in the bundle. 109 | * @param ctx the bundle context used 110 | * @return true, if transactions are missing, false if the bundle is complete 111 | */ 112 | static inline bool bundle_has_open_txs(const BUNDLE_CTX *ctx) 113 | { 114 | return ctx->current_index <= ctx->last_index; 115 | } 116 | 117 | #endif // BUNDLE_H 118 | -------------------------------------------------------------------------------- /src/iota/conversion.h: -------------------------------------------------------------------------------- 1 | /** @file conversion.h 2 | * @brief Function for ternary related conversions. 3 | */ 4 | 5 | #ifndef CONVERSION_H 6 | #define CONVERSION_H 7 | 8 | #include 9 | #include 10 | #include "iota_types.h" 11 | 12 | /** @brief Converts a balanced ternary number in base-27 encoding into its 13 | * trit representation. 14 | * @param chars base-27 encoded ternary number 15 | * @param trits target trit array 16 | * @param chars_len length of the input char array 17 | */ 18 | void chars_to_trits(const char *chars, trit_t *trits, unsigned int chars_len); 19 | 20 | /** @brief Converts a balanced ternary number into base-27 21 | * @param trits input trit array 22 | * @param chars target char array 23 | * @param chars_len length of the input char array 24 | */ 25 | void trits_to_chars(const trit_t *trits, char *chars, unsigned int trit_len); 26 | 27 | /** @brief Converts a single signed integer into its ternary representation. 28 | * @param value signed integer to convert 29 | * @param trits target trit array 30 | * @param num_trits number of trits to convert 31 | * @return true, if an overflow occured and the given integer could not be 32 | completely represented with this number of trits, false otherwise. 33 | */ 34 | bool int64_to_trits(int64_t value, trit_t *trits, unsigned int num_trits); 35 | 36 | /** @brief Converts a balanced ternary number into a big-endian binary integer. 37 | * The input must consist of exactly one 243-trit chunk and is converted into 38 | * one big-endian 48-byte integer. 39 | * @param trits trit array consisting of exectly 243 trits 40 | * @param bytes target byte array 41 | */ 42 | void trits_to_bytes(const trit_t *trits, unsigned char *bytes); 43 | 44 | /** @brief Converts a balanced ternary number in tryte (3-trit) representation 45 | * into a big-endian binary integer. 46 | * The input must consist of exactly one 81-tryte (243-trit) chunk and is 47 | * converted into one big-endian 48-byte integer. 48 | * @param trytes tryte array consisting of exectly 81 trytes 49 | * @param bytes target byte array 50 | */ 51 | void trytes_to_bytes(const tryte_t *trytes, unsigned char *bytes); 52 | 53 | /** @brief Converts a big-endian binary integer into a balanced ternary number 54 | * in tryte (3-trit) representation. 55 | * The input must consist of exactly one big-endian 48-byte integer and is 56 | * converted into one 81-tryte (243-trit) chunk. 57 | * @param bytes input big-endian 48-byte integers 58 | * @param trytes target tryte array 59 | */ 60 | void bytes_to_trytes(const unsigned char *bytes, tryte_t *trytes); 61 | 62 | /** @brief Converts an array of chars into a big-endian binary integer. 63 | * The input must consist of multiples of 81-char chunks, each chunk is 64 | * converted into a big-endian 48-byte integer 65 | * @param chars base-27 encoded ternary number 66 | * @param bytes target byte array 67 | * @param chars_len length of the input 68 | */ 69 | void chars_to_bytes(const char *chars, unsigned char *bytes, unsigned int chars_len); 70 | 71 | /** @brief Converts an array of chars into a balanced ternary number 72 | * in tryte (3-trit) representation. 73 | * The input must consist of multiples of 81-char chunks, each chunk is 74 | * converted into a big-endian 48-byte integer 75 | * @param chars_in base-27 encoded ternary number 76 | * @param trytes_out target tryte array 77 | * @param len length of the input 78 | */ 79 | int chars_to_trytes(const char chars_in[], tryte_t trytes_out[], 80 | unsigned int len); 81 | 82 | /** @brief Converts a big-endian binary integer into a balanced ternary number 83 | * in base-27 encoding. 84 | * The input must consist of one or more big-endian 48-byte integers, each 85 | * integer is sequentially converted into 81 chars and zero-terminated in the end. 86 | * @param bytes input big-endian 48-byte integers 87 | * @param chars zero-terminated base-27 encoded ternary representation 88 | * @param bytes_len number of input bytes 89 | */ 90 | void bytes_to_chars(const unsigned char *bytes, char *chars, unsigned int bytes_len); 91 | 92 | /** @brief Sets the 243th trit to zero. 93 | * If the byte array represents a balanced ternary number which has the 94 | * 243th trit set to +1/-1, the number is adapted to the corresponding 95 | * binary where this trit is 0. 96 | * @param bytes array consisting of 48 bytes. 97 | */ 98 | void bytes_set_last_trit_zero(unsigned char *bytes); 99 | 100 | /** @brief Increment the 82nd trit without carrying overflows across 162nd trit. 101 | * @param bytes array consisting of 48 bytes. 102 | */ 103 | void bytes_increment_trit_area_81(unsigned char *bytes); 104 | 105 | /** @brief Adds a single integer to a 48-byte big-endian integer. 106 | * The bytes are changed in such a way, that they are still a vaild big-endian 107 | * binary representation of a ternary number, i.e. the 243th trit is set to 0. 108 | * @param bytes input big-endian 48-byte integer 109 | * @param summand unsigned number to add 110 | */ 111 | void bytes_add_u32_mem(unsigned char *bytes, uint32_t summand); 112 | 113 | #endif // CONVERSION_H 114 | -------------------------------------------------------------------------------- /tests/pubkey_test.c: -------------------------------------------------------------------------------- 1 | #include "test_common.h" 2 | #include 3 | #include "api_tests.h" 4 | #include "api.h" 5 | #include "iota/conversion.h" 6 | 7 | void derive_seed_bip32(const unsigned int *path, unsigned int pathLength, 8 | unsigned char *seed_bytes) 9 | { 10 | UNUSED(path); 11 | UNUSED(pathLength); 12 | 13 | chars_to_bytes(mock_ptr_type(char *), seed_bytes, NUM_HASH_TRYTES); 14 | } 15 | 16 | void io_send(const void *ptr, unsigned int length, unsigned short sw) 17 | { 18 | check_expected(ptr); 19 | check_expected(length); 20 | check_expected(sw); 21 | } 22 | 23 | static void test_valid_index_level_one(void **state) 24 | { 25 | UNUSED(state); 26 | static const int security = 1; 27 | 28 | for (unsigned idx = 0; idx <= MAX_ADDRESS_INDEX; idx++) { 29 | 30 | SEED_INIT(PETER_VECTOR.seed); 31 | api_initialize(); 32 | { 33 | SET_SEED_INPUT input = {BIP32_PATH, security}; 34 | EXPECT_API_OK(set_seed, input); 35 | } 36 | { 37 | PUBKEY_INPUT input = {idx}; 38 | PUBKEY_OUTPUT output; 39 | strncpy(output.address, PETER_VECTOR.addresses[security][idx], 40 | NUM_HASH_TRYTES); 41 | 42 | EXPECT_API_DATA_OK(pubkey, input, output); 43 | } 44 | } 45 | } 46 | 47 | static void test_valid_index_level_two(void **state) 48 | { 49 | UNUSED(state); 50 | static const int security = 2; 51 | 52 | for (unsigned idx = 0; idx <= MAX_ADDRESS_INDEX; idx++) { 53 | 54 | SEED_INIT(PETER_VECTOR.seed); 55 | api_initialize(); 56 | { 57 | SET_SEED_INPUT input = {BIP32_PATH, security}; 58 | EXPECT_API_OK(set_seed, input); 59 | } 60 | { 61 | PUBKEY_INPUT input = {idx}; 62 | PUBKEY_OUTPUT output; 63 | strncpy(output.address, PETER_VECTOR.addresses[security][idx], 64 | NUM_HASH_TRYTES); 65 | 66 | EXPECT_API_DATA_OK(pubkey, input, output); 67 | } 68 | } 69 | } 70 | 71 | static void test_valid_index_level_three(void **state) 72 | { 73 | UNUSED(state); 74 | static const int security = 3; 75 | 76 | for (unsigned idx = 0; idx <= MAX_ADDRESS_INDEX; idx++) { 77 | 78 | SEED_INIT(PETER_VECTOR.seed); 79 | api_initialize(); 80 | { 81 | SET_SEED_INPUT input = {BIP32_PATH, security}; 82 | EXPECT_API_OK(set_seed, input); 83 | } 84 | { 85 | PUBKEY_INPUT input = {idx}; 86 | PUBKEY_OUTPUT output; 87 | strncpy(output.address, PETER_VECTOR.addresses[security][idx], 88 | NUM_HASH_TRYTES); 89 | 90 | EXPECT_API_DATA_OK(pubkey, input, output); 91 | } 92 | } 93 | } 94 | 95 | static void test_change_security(void **state) 96 | { 97 | UNUSED(state); 98 | 99 | api_initialize(); 100 | { 101 | SEED_INIT(PETER_VECTOR.seed); 102 | SET_SEED_INPUT input = {BIP32_PATH, 1}; 103 | EXPECT_API_OK(set_seed, input); 104 | } 105 | { 106 | SEED_INIT(PETER_VECTOR.seed); 107 | SET_SEED_INPUT input = {BIP32_PATH, 2}; 108 | EXPECT_API_OK(set_seed, input); 109 | } 110 | { 111 | PUBKEY_INPUT input = {0}; 112 | PUBKEY_OUTPUT output; 113 | strncpy(output.address, PETER_VECTOR.addresses[2][0], NUM_HASH_TRYTES); 114 | 115 | EXPECT_API_DATA_OK(pubkey, input, output); 116 | } 117 | } 118 | 119 | static void test_negative_index(void **state) 120 | { 121 | UNUSED(state); 122 | static const int security = 2; 123 | 124 | SEED_INIT(PETER_VECTOR.seed); 125 | api_initialize(); 126 | { 127 | SET_SEED_INPUT input = {BIP32_PATH, security}; 128 | EXPECT_API_OK(set_seed, input); 129 | } 130 | { 131 | PUBKEY_INPUT input = {-1}; 132 | 133 | EXPECT_API_EXCEPTION(pubkey, input); 134 | } 135 | } 136 | 137 | static void test_index_overflow(void **state) 138 | { 139 | UNUSED(state); 140 | static const int security = 2; 141 | 142 | SEED_INIT(PETER_VECTOR.seed); 143 | api_initialize(); 144 | { 145 | SET_SEED_INPUT input = {BIP32_PATH, security}; 146 | EXPECT_API_OK(set_seed, input); 147 | } 148 | { 149 | PUBKEY_INPUT input = {(UINT32_MAX + INT64_C(1))}; 150 | 151 | EXPECT_API_EXCEPTION(pubkey, input); 152 | } 153 | } 154 | 155 | static void test_not_set_seed(void **state) 156 | { 157 | UNUSED(state); 158 | 159 | api_initialize(); 160 | { 161 | PUBKEY_INPUT input = {0}; 162 | 163 | EXPECT_API_EXCEPTION(pubkey, input); 164 | } 165 | } 166 | 167 | int main(void) 168 | { 169 | const struct CMUnitTest tests[] = { 170 | cmocka_unit_test(test_valid_index_level_one), 171 | cmocka_unit_test(test_valid_index_level_two), 172 | cmocka_unit_test(test_valid_index_level_three), 173 | cmocka_unit_test(test_change_security), 174 | cmocka_unit_test(test_negative_index), 175 | cmocka_unit_test(test_index_overflow), 176 | cmocka_unit_test(test_not_set_seed)}; 177 | 178 | return cmocka_run_group_tests(tests, NULL, NULL); 179 | } 180 | -------------------------------------------------------------------------------- /tests/address_test.c: -------------------------------------------------------------------------------- 1 | #include "test_common.h" 2 | #include 3 | #include "test_vectors.h" 4 | #include "hash_file.h" 5 | #include "iota/addresses.h" 6 | #include "iota/conversion.h" 7 | 8 | static void seed_address(const char *seed_chars, uint32_t idx, uint8_t security, 9 | char *address_chars) 10 | { 11 | if (security > 3 || strlen(seed_chars) != NUM_HASH_TRYTES) { 12 | address_chars = ""; 13 | return; 14 | } 15 | 16 | unsigned char seed_bytes[NUM_HASH_BYTES]; 17 | chars_to_bytes(seed_chars, seed_bytes, NUM_HASH_TRYTES); 18 | 19 | unsigned char address_bytes[NUM_HASH_BYTES]; 20 | get_public_addr(seed_bytes, idx, security, address_bytes); 21 | 22 | bytes_to_chars(address_bytes, address_chars, NUM_HASH_BYTES); 23 | // make null-terminated 24 | address_chars[NUM_HASH_TRYTES] = '\0'; 25 | } 26 | 27 | static void test_address(const char *seed, uint32_t idx, uint8_t security, 28 | const char *expected) 29 | { 30 | char output[MAX_NUM_TRYTES + 1]; 31 | seed_address(seed, idx, security, output); 32 | 33 | assert_string_equal(output, expected); 34 | } 35 | 36 | static void test_vector(void **state, const TEST_VECTOR *vector, 37 | uint8_t security) 38 | { 39 | uint32_t idx = (uintptr_t)*state; 40 | assert_in_range(idx, 0, MAX_ADDRESS_INDEX); 41 | 42 | test_address(vector->seed, idx, security, vector->addresses[security][idx]); 43 | } 44 | 45 | static void test_security_level_one(void **state) 46 | { 47 | test_vector(state, &PETER_VECTOR, 1); 48 | } 49 | 50 | static void test_security_level_two(void **state) 51 | { 52 | test_vector(state, &PETER_VECTOR, 2); 53 | } 54 | 55 | static void test_security_level_three(void **state) 56 | { 57 | test_vector(state, &PETER_VECTOR, 3); 58 | } 59 | 60 | static void test_overflow_seed_level_one(void **state) 61 | { 62 | test_vector(state, &OVERFLOW_VECTOR, 1); 63 | } 64 | 65 | static void test_overflow_seed_level_two(void **state) 66 | { 67 | test_vector(state, &OVERFLOW_VECTOR, 2); 68 | } 69 | 70 | static void test_overflow_seed_level_three(void **state) 71 | { 72 | test_vector(state, &OVERFLOW_VECTOR, 3); 73 | } 74 | 75 | static void test_n_addresses_for_seed(void **state) 76 | { 77 | (void)state; // unused 78 | 79 | void test(char *hashes[]) 80 | { 81 | for (uint32_t idx = 0; idx < 4; idx++) { 82 | test_address(hashes[0], idx, 2, hashes[idx + 1]); 83 | } 84 | } 85 | 86 | test_for_each_line("generateNAddressesForSeed", test); 87 | } 88 | 89 | int main(void) 90 | { 91 | const struct CMUnitTest tests[] = { 92 | cmocka_unit_test_prestate(test_security_level_one, (uint32_t *)0), 93 | cmocka_unit_test_prestate(test_security_level_one, (uint32_t *)1), 94 | cmocka_unit_test_prestate(test_security_level_one, (uint32_t *)2), 95 | cmocka_unit_test_prestate(test_security_level_one, (uint32_t *)3), 96 | cmocka_unit_test_prestate(test_security_level_one, (uint32_t *)4), 97 | cmocka_unit_test_prestate(test_security_level_two, (uint32_t *)0), 98 | cmocka_unit_test_prestate(test_security_level_two, (uint32_t *)1), 99 | cmocka_unit_test_prestate(test_security_level_two, (uint32_t *)2), 100 | cmocka_unit_test_prestate(test_security_level_two, (uint32_t *)3), 101 | cmocka_unit_test_prestate(test_security_level_two, (uint32_t *)4), 102 | cmocka_unit_test_prestate(test_security_level_three, (uint32_t *)0), 103 | cmocka_unit_test_prestate(test_security_level_three, (uint32_t *)1), 104 | cmocka_unit_test_prestate(test_security_level_three, (uint32_t *)2), 105 | cmocka_unit_test_prestate(test_security_level_three, (uint32_t *)3), 106 | cmocka_unit_test_prestate(test_security_level_three, (uint32_t *)4), 107 | cmocka_unit_test_prestate(test_overflow_seed_level_one, (uint32_t *)0), 108 | cmocka_unit_test_prestate(test_overflow_seed_level_one, (uint32_t *)1), 109 | cmocka_unit_test_prestate(test_overflow_seed_level_one, (uint32_t *)2), 110 | cmocka_unit_test_prestate(test_overflow_seed_level_one, (uint32_t *)3), 111 | cmocka_unit_test_prestate(test_overflow_seed_level_one, (uint32_t *)4), 112 | cmocka_unit_test_prestate(test_overflow_seed_level_two, (uint32_t *)0), 113 | cmocka_unit_test_prestate(test_overflow_seed_level_two, (uint32_t *)1), 114 | cmocka_unit_test_prestate(test_overflow_seed_level_two, (uint32_t *)2), 115 | cmocka_unit_test_prestate(test_overflow_seed_level_two, (uint32_t *)3), 116 | cmocka_unit_test_prestate(test_overflow_seed_level_two, (uint32_t *)4), 117 | cmocka_unit_test_prestate(test_overflow_seed_level_three, 118 | (uint32_t *)0), 119 | cmocka_unit_test_prestate(test_overflow_seed_level_three, 120 | (uint32_t *)1), 121 | cmocka_unit_test_prestate(test_overflow_seed_level_three, 122 | (uint32_t *)2), 123 | cmocka_unit_test_prestate(test_overflow_seed_level_three, 124 | (uint32_t *)3), 125 | cmocka_unit_test_prestate(test_overflow_seed_level_three, 126 | (uint32_t *)4), 127 | cmocka_unit_test(test_n_addresses_for_seed)}; 128 | 129 | return cmocka_run_group_tests(tests, NULL, NULL); 130 | } 131 | -------------------------------------------------------------------------------- /tests/kerl_test.c: -------------------------------------------------------------------------------- 1 | #include "test_common.h" 2 | #include 3 | #include 4 | #include "hash_file.h" 5 | #include "iota/conversion.h" 6 | #include "iota/kerl.h" 7 | 8 | static void kerl(const char *input, const size_t squeeze_num_trits, 9 | char *output) 10 | { 11 | const size_t num_trytes = strlen(input); 12 | const size_t num_trits = num_trytes * TRITS_PER_TRYTE; 13 | 14 | unsigned char bytes[MAX_NUM_BYTES]; 15 | chars_to_bytes(input, bytes, num_trytes); 16 | 17 | cx_sha3_t kerl; 18 | 19 | kerl_initialize(&kerl); 20 | kerl_absorb_bytes(&kerl, bytes, NUM_BYTES(num_trits)); 21 | kerl_squeeze_bytes(&kerl, bytes, NUM_BYTES(squeeze_num_trits)); 22 | 23 | bytes_to_chars(bytes, output, NUM_BYTES(squeeze_num_trits)); 24 | // make null-terminated 25 | output[NUM_TRYTES(squeeze_num_trits)] = '\0'; 26 | } 27 | 28 | static void test_kerl(const char *input, const size_t length, 29 | const char *expected) 30 | { 31 | char output[MAX_NUM_TRYTES + 1]; 32 | 33 | assert_non_null(kerl); 34 | kerl(input, length, output); 35 | 36 | assert_string_equal(output, expected); 37 | } 38 | 39 | static void test_peter_seed(void **state) 40 | { 41 | (void)state; // unused 42 | 43 | test_kerl( 44 | "PETERPETERPETERPETERPETERPETERPETERPETERPETERPETERPETERPETERPETERPETER" 45 | "PETERPETERR", 46 | 243, 47 | "JPXGSMTSWTIOVEMLNVWNUPDLZPCF9AARZTSBT9SUNTRHIHWNVB9SPXFHWLY9OCSPLBELQG" 48 | "IFYDXG9OHOC"); 49 | } 50 | 51 | static void test_243_neg_one(void **state) 52 | { 53 | (void)state; // unused 54 | 55 | test_kerl( 56 | "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN" 57 | "NNNNNNNNNNN", 58 | 243, 59 | "NXOQOASTBGO9BF9YZRFHTALRUVRLRYPKDUIZJJLKLVTSMERZQBAQOSMGUE9LIDPXJJWAAB" 60 | "TIYNTURURTD"); 61 | } 62 | 63 | static void test_242_neg_one(void **state) 64 | { 65 | (void)state; // unused 66 | 67 | test_kerl( 68 | "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN" 69 | "NNNNNNNNNNW", 70 | 243, 71 | "NXOQOASTBGO9BF9YZRFHTALRUVRLRYPKDUIZJJLKLVTSMERZQBAQOSMGUE9LIDPXJJWAAB" 72 | "TIYNTURURTD"); 73 | } 74 | 75 | static void test_input_with_243trits(void **state) 76 | { 77 | (void)state; // unused 78 | 79 | test_kerl( 80 | "EMIDYNHBWMBCXVDEFOFWINXTERALUKYYPPHKP9JJFGJEIUY9MUDVNFZHMMWZUYUSWAIOWE" 81 | "VTHNWMHANBH", 82 | 243, 83 | "EJEAOOZYSAWFPZQESYDHZCGYNSTWXUMVJOVDWUNZJXDGWCLUFGIMZRMGCAZGKNPLBRLGUN" 84 | "YWKLJTYEAQX"); 85 | } 86 | 87 | static void test_output_with_more_than_243trits(void **state) 88 | { 89 | (void)state; // unused 90 | 91 | test_kerl( 92 | "9MIDYNHBWMBCXVDEFOFWINXTERALUKYYPPHKP9JJFGJEIUY9MUDVNFZHMMWZUYUSWAIOWE" 93 | "VTHNWMHANBH", 94 | 486, 95 | "G9JYBOMPUXHYHKSNRNMMSSZCSHOFYOYNZRSZMAAYWDYEIMVVOGKPJBVBM9TDPULSFUNMTV" 96 | "XRKFIDOHUXXVYDLFSZYZTWQYTE9SPYYWYTXJYQ9IFGYOLZXWZBKWZN9QOOTBQMWMUBLEWU" 97 | "EEASRHRTNIQWJQNDWRYLCA"); 98 | } 99 | 100 | static void test_input_output_with_more_than_243trits(void **state) 101 | { 102 | (void)state; // unused 103 | 104 | test_kerl( 105 | "G9JYBOMPUXHYHKSNRNMMSSZCSHOFYOYNZRSZMAAYWDYEIMVVOGKPJBVBM9TDPULSFUNMTV" 106 | "XRKFIDOHUXXVYDLFSZYZTWQYTE9SPYYWYTXJYQ9IFGYOLZXWZBKWZN9QOOTBQMWMUBLEWU" 107 | "EEASRHRTNIQWJQNDWRYLCA", 108 | 486, 109 | "LUCKQVACOGBFYSPPVSSOXJEKNSQQRQKPZC9NXFSMQNRQCGGUL9OHVVKBDSKEQEBKXRNUJS" 110 | "RXYVHJTXBPDWQGNSCDCBAIRHAQCOWZEBSNHIJIGPZQITIBJQ9LNTDIBTCQ9EUWKHFLGFUV" 111 | "GGUWJONK9GBCDUIMAYMMQX"); 112 | } 113 | 114 | static void test_generate_trytes_and_hashes(void **state) 115 | { 116 | (void)state; // unused 117 | 118 | void test(char *hashes[]) 119 | { 120 | test_kerl(hashes[0], NUM_HASH_TRITS, hashes[1]); 121 | } 122 | 123 | test_for_each_line("generateTrytesAndHashes", test); 124 | } 125 | 126 | static void test_generate_multi_trytes_and_hash(void **state) 127 | { 128 | (void)state; // unused 129 | 130 | void test(char *hashes[]) 131 | { 132 | test_kerl(hashes[0], NUM_HASH_TRITS, hashes[1]); 133 | } 134 | 135 | test_for_each_line("generateMultiTrytesAndHash", test); 136 | } 137 | 138 | static void test_generate_trytes_and_multi_squeeze(void **state) 139 | { 140 | (void)state; // unused 141 | 142 | void test(char *hashes[]) 143 | { 144 | char str[1024]; 145 | snprintf(str, sizeof(str), "%s%s%s", hashes[1], hashes[2], hashes[3]); 146 | 147 | test_kerl(hashes[0], 3 * NUM_HASH_TRITS, str); 148 | } 149 | 150 | test_for_each_line("generateTrytesAndMultiSqueeze", test); 151 | } 152 | 153 | int main(void) 154 | { 155 | const struct CMUnitTest tests[] = { 156 | cmocka_unit_test(test_peter_seed), 157 | cmocka_unit_test(test_243_neg_one), 158 | cmocka_unit_test(test_242_neg_one), 159 | cmocka_unit_test(test_input_with_243trits), 160 | cmocka_unit_test(test_output_with_more_than_243trits), 161 | cmocka_unit_test(test_input_output_with_more_than_243trits), 162 | cmocka_unit_test(test_generate_trytes_and_hashes), 163 | cmocka_unit_test(test_generate_multi_trytes_and_hash), 164 | cmocka_unit_test(test_generate_trytes_and_multi_squeeze)}; 165 | 166 | return cmocka_run_group_tests(tests, NULL, NULL); 167 | } 168 | -------------------------------------------------------------------------------- /tests/sign_test.c: -------------------------------------------------------------------------------- 1 | #include "test_common.h" 2 | #include 3 | #include "api_tests.h" 4 | #include "api.h" 5 | #include "transaction_file.h" 6 | #include "iota/conversion.h" 7 | #include "iota/signing.h" 8 | 9 | void derive_seed_bip32(const unsigned int *path, unsigned int pathLength, 10 | unsigned char *seed_bytes) 11 | { 12 | UNUSED(path); 13 | UNUSED(pathLength); 14 | 15 | chars_to_bytes(mock_ptr_type(char *), seed_bytes, NUM_HASH_TRYTES); 16 | } 17 | 18 | void io_send(const void *ptr, unsigned int length, unsigned short sw) 19 | { 20 | check_expected(ptr); 21 | check_expected(length); 22 | check_expected(sw); 23 | } 24 | 25 | static void input_valid_bundle(int security, const TX_INPUT *tx, int last_index, 26 | const char *bundle_hash) 27 | { 28 | { 29 | SET_SEED_INPUT input = {BIP32_PATH, security}; 30 | EXPECT_API_OK(set_seed, input); 31 | } 32 | for (int i = 0; i < last_index; i++) { 33 | TX_OUTPUT output = {0}; 34 | output.finalized = false; 35 | 36 | EXPECT_API_DATA_OK(tx, tx[i], output); 37 | } 38 | { 39 | TX_OUTPUT output = {0}; 40 | output.finalized = true; 41 | strncpy(output.bundle_hash, bundle_hash, 81); 42 | 43 | EXPECT_API_DATA_OK(tx, tx[last_index], output); 44 | } 45 | } 46 | 47 | static void test_valid_signatures(const char *seed, int security, 48 | const TX_INPUT *tx, int last_index, 49 | const char *bundle_hash, 50 | const char signature[][SIGNATURE_LENGTH]) 51 | { 52 | const int num_fragments = NUM_SIGNATURE_FRAGMENTS(security); 53 | 54 | SEED_INIT(seed); 55 | api_initialize(); 56 | input_valid_bundle(security, tx, last_index, bundle_hash); 57 | 58 | for (int i = 0; i < 2; i++) { 59 | for (int j = 0; j < num_fragments; j++) { 60 | SIGN_INPUT input; 61 | input.transaction_idx = 1 + i * security; 62 | 63 | SIGN_OUTPUT output; 64 | output.fragments_remaining = (j + 1) != num_fragments; 65 | memcpy(output.signature_fragment, signature[i] + j * 243, 243); 66 | 67 | EXPECT_API_DATA_OK(sign, input, output); 68 | } 69 | } 70 | } 71 | 72 | static void test_signatures_for_seed_from_file(void **state) 73 | { 74 | UNUSED(state); 75 | 76 | void test(char *seed, TX_INPUT *tx, char *bundle_hash, 77 | char signature[][SIGNATURE_LENGTH]) 78 | { 79 | test_valid_signatures(seed, 2, tx, 5, bundle_hash, signature); 80 | } 81 | 82 | test_for_each_bundle("generateBundlesForSeed", test); 83 | } 84 | 85 | static void test_unfinalized_bundle(void **state) 86 | { 87 | UNUSED(state); 88 | 89 | SEED_INIT(PETER_VECTOR.seed); 90 | api_initialize(); 91 | { 92 | SET_SEED_INPUT input = {BIP32_PATH, 2}; 93 | EXPECT_API_OK(set_seed, input); 94 | } 95 | { 96 | TX_INPUT input; 97 | memcpy(&input, &PETER_VECTOR.bundle[0], sizeof(input)); 98 | TX_OUTPUT output = {0}; 99 | output.finalized = false; 100 | 101 | EXPECT_API_DATA_OK(tx, input, output); 102 | } 103 | { 104 | SIGN_INPUT input; 105 | input.transaction_idx = 0; 106 | 107 | EXPECT_API_EXCEPTION(sign, input); 108 | } 109 | } 110 | 111 | static void test_output_index(void **state) 112 | { 113 | UNUSED(state); 114 | 115 | SEED_INIT(PETER_VECTOR.seed); 116 | api_initialize(); 117 | input_valid_bundle(2, PETER_VECTOR.bundle, 2, PETER_VECTOR.bundle_hash); 118 | { 119 | SIGN_INPUT input; 120 | input.transaction_idx = 0; 121 | 122 | EXPECT_API_EXCEPTION(sign, input); 123 | } 124 | } 125 | 126 | static void test_meta_index(void **state) 127 | { 128 | UNUSED(state); 129 | 130 | SEED_INIT(PETER_VECTOR.seed); 131 | api_initialize(); 132 | input_valid_bundle(2, PETER_VECTOR.bundle, 2, PETER_VECTOR.bundle_hash); 133 | { 134 | SIGN_INPUT input; 135 | input.transaction_idx = 2; 136 | 137 | EXPECT_API_EXCEPTION(sign, input); 138 | } 139 | } 140 | 141 | static void test_changing_index(void **state) 142 | { 143 | UNUSED(state); 144 | 145 | SEED_INIT(PETER_VECTOR.seed); 146 | api_initialize(); 147 | input_valid_bundle(2, PETER_VECTOR.bundle, 2, PETER_VECTOR.bundle_hash); 148 | { 149 | SIGN_INPUT input; 150 | input.transaction_idx = 1; 151 | 152 | SIGN_OUTPUT output; 153 | output.fragments_remaining = true; 154 | memcpy(output.signature_fragment, PETER_VECTOR.signature[0], 243); 155 | 156 | EXPECT_API_DATA_OK(sign, input, output); 157 | } 158 | { 159 | SIGN_INPUT input; 160 | input.transaction_idx = 2; 161 | 162 | EXPECT_API_EXCEPTION(sign, input); 163 | } 164 | } 165 | 166 | static void test_not_set_seed(void **state) 167 | { 168 | UNUSED(state); 169 | 170 | api_initialize(); 171 | { 172 | SIGN_INPUT input; 173 | input.transaction_idx = 0; 174 | 175 | EXPECT_API_EXCEPTION(sign, input); 176 | } 177 | } 178 | 179 | int main(void) 180 | { 181 | const struct CMUnitTest tests[] = { 182 | cmocka_unit_test(test_signatures_for_seed_from_file), 183 | cmocka_unit_test(test_unfinalized_bundle), 184 | cmocka_unit_test(test_output_index), 185 | cmocka_unit_test(test_meta_index), 186 | cmocka_unit_test(test_changing_index), 187 | cmocka_unit_test(test_not_set_seed)}; 188 | 189 | return cmocka_run_group_tests(tests, NULL, NULL); 190 | } 191 | -------------------------------------------------------------------------------- /install_dev_env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## Functions 4 | _bold=$(tput bold) 5 | _reset=$(tput sgr0) 6 | 7 | _red=$(tput setaf 1) 8 | _green=$(tput setaf 2) 9 | _yellow=$(tput setaf 3) 10 | 11 | function print_ok { printf "${_bold}${_green}%s${_reset}\n" "$@"; } 12 | function print_err { printf "${_bold}${_red}%s${_reset}\n" "$@"; } 13 | function print_warn { printf "${_bold}${_yellow}%s${_reset}\n" "$@"; } 14 | 15 | ## Linux Distribution and Version Check 16 | print_ok "Installing development environment for blue-app-iota..." 17 | 18 | if ! type "gawk" &> /dev/null; then 19 | print_err "ERROR: gawk is missing! If you're on Ubuntu, please run 'sudo apt install -y gawk'" 20 | exit 1 21 | fi 22 | 23 | linux_distr=`gawk -F= '/^NAME/{print $2}' /etc/os-release` 24 | linux_codename=`gawk -F= '/^VERSION_CODENAME/{print $2}' /etc/os-release` 25 | if [ ${linux_distr} != '"Ubuntu"' ]; 26 | then 27 | print_err "ERROR: This script was created for Ubuntu only! Exiting..." 28 | exit 1 29 | fi 30 | 31 | if [ ${linux_codename} != 'artful' ]; 32 | then 33 | print_err "ERROR: This script was tested under Ubuntu Artful Aardvark only! Exiting..." 34 | exit 1 35 | fi 36 | 37 | ## Variables 38 | BLUE_APP_IOTA_PY_VIRT_ENV=`realpath .pyenv` 39 | export BOLOS_SDK=`realpath ../nanos-secure-sdk` 40 | export BOLOS_ENV=`realpath ../bolos-devenv` 41 | DIR_ARM_GCC=${BOLOS_ENV}/gcc-arm-none-eabi-5_3-2016q1/ 42 | DIR_CLANG=${BOLOS_ENV}/clang-arm-fropi/ 43 | 44 | print_ok "Updating Packages..." 45 | sudo apt update && sudo apt dist-upgrade -y 46 | 47 | print_ok "Installing build-essential, libc6, libudev, libusb and Python3 virtual environment..." 48 | sudo apt install -y build-essential libc6-i386 libc6-dev-i386 libudev-dev libusb-1.0-0-dev virtualenv python3-dev python3-pip python3-virtualenv python3-venv 49 | 50 | if [ ! -d ${BOLOS_ENV} ]; then 51 | mkdir -p ${BOLOS_ENV} 52 | fi 53 | 54 | if [ ! -d ${DIR_ARM_GCC} ]; then 55 | print_ok "Downloading \"gcc-arm-none-eabi-5_3-2016q1\"..." 56 | wget -N https://launchpad.net/gcc-arm-embedded/5.0/5-2016-q1-update/+download/gcc-arm-none-eabi-5_3-2016q1-20160330-linux.tar.bz2 -P ${BOLOS_ENV}/ 57 | 58 | print_ok "Unzipping \"gcc-arm-none-eabi-5_3-2016q1\"..." 59 | tar xf ${BOLOS_ENV}/gcc-arm-none-eabi-5_3-2016q1-20160330-linux.tar.bz2 -C ${BOLOS_ENV}/ 60 | fi 61 | 62 | if [ ! -d ${DIR_CLANG} ]; then 63 | print_ok "Downloading \"clang+llvm-4.0.0\"..." 64 | wget -N http://releases.llvm.org/4.0.0/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-16.10.tar.xz -P ${BOLOS_ENV}/ 65 | 66 | print_ok "Unzipping \"clang+llvm-4.0.0\"..." 67 | tar xf ${BOLOS_ENV}/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-16.10.tar.xz -C ${BOLOS_ENV}/ 68 | mv ${BOLOS_ENV}/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-16.10 ${BOLOS_ENV}/clang-arm-fropi 69 | fi 70 | 71 | if [ -d ${BOLOS_SDK} ]; then 72 | print_ok "Updating nanos-secure-sdk..." 73 | cd ${BOLOS_SDK} 74 | git pull origin master 75 | git checkout tags/nanos-141 76 | else 77 | print_ok "Downloading nanos-secure-sdk..." 78 | cd .. 79 | git clone https://github.com/LedgerHQ/nanos-secure-sdk.git 80 | git checkout tags/nanos-141 81 | fi 82 | 83 | ## Adding environment variables t0 .bashrc 84 | if grep -q '^export BOLOS_SDK=' ~/.bashrc 85 | then 86 | # change BOLOS_SDK 87 | print_ok "Updating \"BOLOS_SDK\" environment variable in .bashrc..." 88 | sed -i -e "s;.*export BOLOS_SDK=.*;export BOLOS_SDK=\"${BOLOS_SDK}\";" ~/.bashrc 89 | else 90 | # add BOLOS_SDK 91 | print_ok "Adding \"BOLOS_SDK\" environment variable to .bashrc..." 92 | sed -i -e "$ a\export BOLOS_SDK=\"${BOLOS_SDK}\"" ~/.bashrc 93 | fi 94 | 95 | if grep -q '^export BOLOS_ENV=' ~/.bashrc 96 | then 97 | # change BOLOS_ENV 98 | print_ok "Updating \"BOLOS_ENV\" environment variable in .bashrc..." 99 | sed -i -e "s;.*export BOLOS_ENV=.*;export BOLOS_ENV=\"${BOLOS_ENV}\";" ~/.bashrc 100 | else 101 | # add BOLOS_ENV 102 | print_ok "Adding \"BOLOS_ENV\" environment variable to .bashrc..." 103 | sed -i -e "$ a\export BOLOS_ENV=\"${BOLOS_ENV}\"" ~/.bashrc 104 | fi 105 | 106 | print_ok "Creating python virtual environment..." 107 | python3 -m venv ${BLUE_APP_IOTA_PY_VIRT_ENV} 108 | . ${BLUE_APP_IOTA_PY_VIRT_ENV}/bin/activate 109 | pip3 install setuptools --upgrade 110 | pip3 install wheel 111 | #pip3 install ledgerblue # This doesn't work at the moment => Better use the git repo! 112 | SECP_BUNDLED_EXPERIMENTAL=1 pip3 --no-cache-dir install --no-binary secp256k1 secp256k1 113 | pip3 install git+https://github.com/LedgerHQ/blue-loader-python.git 114 | 115 | print_ok "Adding user to the \"plugdev\" group..." 116 | sudo adduser $USER plugdev 117 | 118 | print_ok "Installing udev rules for ledger nano S..." 119 | # Additional Tags (if ledger is no recognized) => TAG+="uaccess", TAG+="udev-acl" OWNER="" 120 | sudo bash -c "echo 'SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"2581\", ATTRS{idProduct}==\"1b7c\", MODE=\"0660\", GROUP=\"plugdev\" 121 | SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"2581\", ATTRS{idProduct}==\"2b7c\", MODE=\"0660\", GROUP=\"plugdev\" 122 | SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"2581\", ATTRS{idProduct}==\"3b7c\", MODE=\"0660\", GROUP=\"plugdev\" 123 | SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"2581\", ATTRS{idProduct}==\"4b7c\", MODE=\"0660\", GROUP=\"plugdev\" 124 | SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"2581\", ATTRS{idProduct}==\"1807\", MODE=\"0660\", GROUP=\"plugdev\" 125 | SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"2581\", ATTRS{idProduct}==\"1808\", MODE=\"0660\", GROUP=\"plugdev\" 126 | SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"2c97\", ATTRS{idProduct}==\"0000\", MODE=\"0660\", GROUP=\"plugdev\" 127 | SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"2c97\", ATTRS{idProduct}==\"0001\", MODE=\"0660\", GROUP=\"plugdev\"' >/etc/udev/rules.d/20-hw1.rules" 128 | sudo udevadm trigger 129 | sudo udevadm control --reload-rules 130 | 131 | print_ok "...Installing development environment for blue-app-iota done!" 132 | print_warn "Please restart to apply changes!" 133 | -------------------------------------------------------------------------------- /tests/conversion_test.c: -------------------------------------------------------------------------------- 1 | #include "test_common.h" 2 | #include 3 | // include the c-file to be able to test static functions 4 | #include "iota/conversion.c" 5 | 6 | #define NUM_RANDOM_TESTS 10000 7 | 8 | static void test_increment_trit_82(void **state) 9 | { 10 | UNUSED(state); 11 | 12 | // all zero trits correspond to all zero bytes 13 | unsigned char bytes[NUM_HASH_BYTES] = {0}; 14 | 15 | bytes_increment_trit_area_81(bytes); 16 | 17 | trit_t inc_trits[NUM_HASH_TRITS]; 18 | bytes_to_trits(bytes, inc_trits); 19 | 20 | trit_t expected_trits[NUM_HASH_TRITS] = {0}; 21 | // the 82nd trit is the trit at index 81 22 | expected_trits[81] = 1; 23 | 24 | assert_memory_equal(inc_trits, expected_trits, NUM_HASH_TRITS); 25 | } 26 | 27 | static void test_increment_trit_no_overflow(void **state) 28 | { 29 | UNUSED(state); 30 | 31 | trit_t trits[NUM_HASH_TRITS] = {0}; 32 | memset(trits + 81, 1, 81); 33 | 34 | unsigned char bytes[NUM_HASH_BYTES]; 35 | trits_to_bytes(trits, bytes); 36 | 37 | bytes_increment_trit_area_81(bytes); 38 | 39 | trit_t inc_trits[NUM_HASH_TRITS]; 40 | bytes_to_trits(bytes, inc_trits); 41 | 42 | trit_t expected_trits[NUM_HASH_TRITS] = {0}; 43 | memset(expected_trits + 81, -1, 81); 44 | 45 | assert_memory_equal(inc_trits, expected_trits, NUM_HASH_TRITS); 46 | } 47 | 48 | static void test_int64_to_trits_zero(void **state) 49 | { 50 | UNUSED(state); 51 | 52 | static const int64_t input_value = 0; 53 | static const trit_t expected_trits[42] = {0}; 54 | 55 | trit_t trits_out[42]; 56 | bool result = int64_to_trits(input_value, trits_out, 42); 57 | 58 | assert_false(result); 59 | assert_memory_equal(trits_out, expected_trits, 42); 60 | } 61 | 62 | static void test_int64_to_trits_one(void **state) 63 | { 64 | UNUSED(state); 65 | 66 | static const int64_t input_value = 6078832729528464400; 67 | trit_t expected_trits[40]; 68 | memset(expected_trits, 1, 40); 69 | 70 | trit_t trits_out[40]; 71 | bool result = int64_to_trits(input_value, trits_out, 40); 72 | 73 | assert_false(result); 74 | assert_memory_equal(trits_out, expected_trits, 40); 75 | } 76 | 77 | static void test_int64_to_trits_neg_one(void **state) 78 | { 79 | UNUSED(state); 80 | 81 | static const int64_t input_value = -6078832729528464400; 82 | trit_t expected_trits[40]; 83 | memset(expected_trits, -1, 40); 84 | 85 | trit_t trits_out[40]; 86 | bool result = int64_to_trits(input_value, trits_out, 40); 87 | 88 | assert_false(result); 89 | assert_memory_equal(trits_out, expected_trits, 40); 90 | } 91 | 92 | static void test_int64_to_trits_overflow(void **state) 93 | { 94 | UNUSED(state); 95 | 96 | static const int64_t input_value = 6078832729528464401; 97 | 98 | trit_t trits_out[40]; 99 | bool result = int64_to_trits(input_value, trits_out, 40); 100 | 101 | assert_true(result); 102 | } 103 | 104 | static void random_bytes(unsigned char *bytes) 105 | { 106 | for (int i = 0; i < NUM_HASH_BYTES; i++) { 107 | bytes[i] = rand() & 0xFF; 108 | } 109 | } 110 | 111 | static void random_chars(char *chars) 112 | { 113 | for (int i = 0; i < NUM_HASH_TRYTES; i++) { 114 | const int rn = rand() % 27; 115 | if (rn == 26) { 116 | chars[i] = '9'; 117 | } 118 | else { 119 | chars[i] = 'A' + rn; 120 | } 121 | } 122 | } 123 | 124 | static void assert_bytes_equal(unsigned char *actual, unsigned char *expected) 125 | { 126 | bytes_set_last_trit_zero(actual); 127 | bytes_set_last_trit_zero(expected); 128 | 129 | assert_memory_equal(actual, expected, NUM_HASH_BYTES); 130 | } 131 | 132 | static void test_random_bytes_via_chars(void **state) 133 | { 134 | UNUSED(state); 135 | 136 | srand(2); 137 | for (uint i = 0; i < NUM_RANDOM_TESTS; i++) { 138 | unsigned char in_bytes[NUM_HASH_BYTES]; 139 | random_bytes(in_bytes); 140 | 141 | char chars[82]; 142 | bytes_to_chars(in_bytes, chars, NUM_HASH_BYTES); 143 | 144 | unsigned char out_bytes[NUM_HASH_BYTES]; 145 | chars_to_bytes(chars, out_bytes, NUM_HASH_TRYTES); 146 | 147 | assert_bytes_equal(in_bytes, out_bytes); 148 | } 149 | } 150 | 151 | static void assert_chars_equal(const char *actual, const char *expected) 152 | { 153 | trit_t actual_trits[NUM_HASH_TRITS]; 154 | chars_to_trits(actual, actual_trits, NUM_HASH_TRYTES); 155 | 156 | trit_t expected_trits[NUM_HASH_TRITS]; 157 | chars_to_trits(expected, expected_trits, NUM_HASH_TRYTES); 158 | 159 | // ignore 243th trit 160 | assert_memory_equal(actual_trits, expected_trits, 242); 161 | } 162 | 163 | static void test_chars_via_bytes(const char *input) 164 | { 165 | unsigned char bytes[NUM_HASH_BYTES]; 166 | chars_to_bytes(input, bytes, NUM_HASH_TRYTES); 167 | 168 | char chars[82]; 169 | bytes_to_chars(bytes, chars, NUM_HASH_BYTES); 170 | 171 | assert_chars_equal(chars, input); 172 | } 173 | 174 | static void test_all_zero(void **state) 175 | { 176 | UNUSED(state); 177 | 178 | static const char ZERO_CHARS[NUM_HASH_TRYTES] = 179 | "9999999999999999999999999999999999999999999999999999999999999999999999" 180 | "99999999999"; 181 | static const unsigned char ZERO_BYTES[NUM_HASH_BYTES] = {0}; 182 | 183 | unsigned char bytes[NUM_HASH_BYTES]; 184 | chars_to_bytes(ZERO_CHARS, bytes, NUM_HASH_TRYTES); 185 | 186 | assert_memory_equal(bytes, ZERO_BYTES, NUM_HASH_BYTES); 187 | 188 | char chars[82]; 189 | bytes_to_chars(bytes, chars, NUM_HASH_BYTES); 190 | 191 | assert_chars_equal(chars, ZERO_CHARS); 192 | } 193 | 194 | static void test_all_neg_one(void **state) 195 | { 196 | UNUSED(state); 197 | 198 | static const char NEG_ONE_CHARS[NUM_HASH_TRYTES] = 199 | "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN" 200 | "NNNNNNNNNNN"; 201 | 202 | test_chars_via_bytes(NEG_ONE_CHARS); 203 | } 204 | 205 | static void test_random_chars_via_bytes(void **state) 206 | { 207 | (void)state; // unused 208 | 209 | srand(2); 210 | for (uint i = 0; i < NUM_RANDOM_TESTS; i++) { 211 | char in_chars[NUM_HASH_TRYTES]; 212 | random_chars(in_chars); 213 | 214 | test_chars_via_bytes(in_chars); 215 | } 216 | } 217 | 218 | int main(void) 219 | { 220 | const struct CMUnitTest tests[] = { 221 | cmocka_unit_test(test_increment_trit_82), 222 | cmocka_unit_test(test_increment_trit_no_overflow), 223 | cmocka_unit_test(test_int64_to_trits_zero), 224 | cmocka_unit_test(test_int64_to_trits_one), 225 | cmocka_unit_test(test_int64_to_trits_neg_one), 226 | cmocka_unit_test(test_int64_to_trits_overflow), 227 | cmocka_unit_test(test_all_zero), 228 | cmocka_unit_test(test_all_neg_one), 229 | cmocka_unit_test(test_random_bytes_via_chars), 230 | cmocka_unit_test(test_random_chars_via_bytes)}; 231 | 232 | return cmocka_run_group_tests(tests, NULL, NULL); 233 | } 234 | -------------------------------------------------------------------------------- /src/iota/transfers.c: -------------------------------------------------------------------------------- 1 | #include "transfers.h" 2 | 3 | #include 4 | #include 5 | // iota-related stuff 6 | #include "conversion.h" 7 | #include "addresses.h" 8 | #include "bundle.h" 9 | #include "signing.h" 10 | #include "../aux.h" 11 | 12 | #define ZERO_HASH \ 13 | "999999999999999999999999999999999999999999999999999999999999999999999999" \ 14 | "999999999" 15 | #define ZERO_TAG "999999999999999999999999999" 16 | 17 | typedef struct TX_OBJECT { 18 | char signatureMessageFragment[2187]; 19 | char address[81]; 20 | int64_t value; 21 | char obsoleteTag[27]; 22 | uint32_t timestamp; 23 | uint32_t currentIndex; 24 | uint32_t lastIndex; 25 | char bundle[81]; 26 | char trunkTransaction[81]; 27 | char branchTransaction[81]; 28 | char tag[27]; 29 | uint32_t attachmentTimestamp; 30 | uint32_t attachmentTimestampLowerBound; 31 | uint32_t attachmentTimestampUpperBound; 32 | char nonce[27]; 33 | } TX_OBJECT; 34 | 35 | static const TX_OBJECT DEFAULT_TX = { 36 | {0}, ZERO_HASH, 0, ZERO_TAG, 0, 0, 0, ZERO_HASH, 37 | ZERO_HASH, ZERO_HASH, ZERO_TAG, 0, 0, 0, ZERO_TAG}; 38 | 39 | static char *int64_to_chars(int64_t value, char *chars, unsigned int num_trytes) 40 | { 41 | trit_t trits[num_trytes * 3]; 42 | int64_to_trits(value, trits, num_trytes * 3); 43 | trits_to_chars(trits, chars, num_trytes * 3); 44 | 45 | return chars + num_trytes; 46 | } 47 | 48 | static void get_address(const unsigned char *seed_bytes, uint32_t idx, 49 | unsigned int security, char *address) 50 | { 51 | unsigned char bytes[48]; 52 | get_public_addr(seed_bytes, idx, security, bytes); 53 | bytes_to_chars(bytes, address, 48); 54 | } 55 | 56 | static char *char_copy(char *destination, const char *source, unsigned int len) 57 | { 58 | assert(strnlen(source, len) == len); 59 | memcpy(destination, source, len); 60 | 61 | return destination + len; 62 | } 63 | 64 | static void get_transaction_chars(const TX_OBJECT tx, char *transaction_chars) 65 | { 66 | // just to make sure 67 | memset(transaction_chars, '\0', 2673); 68 | 69 | char *c = transaction_chars; 70 | 71 | c = char_copy(c, tx.signatureMessageFragment, 2187); 72 | c = char_copy(c, tx.address, 81); 73 | c = int64_to_chars(tx.value, c, 27); 74 | c = char_copy(c, tx.obsoleteTag, 27); 75 | c = int64_to_chars(tx.timestamp, c, 9); 76 | c = int64_to_chars(tx.currentIndex, c, 9); 77 | c = int64_to_chars(tx.lastIndex, c, 9); 78 | c = char_copy(c, tx.bundle, 81); 79 | c = char_copy(c, tx.trunkTransaction, 81); 80 | c = char_copy(c, tx.branchTransaction, 81); 81 | c = char_copy(c, tx.tag, 27); 82 | c = int64_to_chars(tx.attachmentTimestamp, c, 9); 83 | c = int64_to_chars(tx.attachmentTimestampLowerBound, c, 9); 84 | c = int64_to_chars(tx.attachmentTimestampUpperBound, c, 9); 85 | char_copy(c, tx.nonce, 27); 86 | } 87 | 88 | static void increment_obsolete_tag(unsigned int tag_increment, TX_OBJECT *tx) 89 | { 90 | char extended_tag[81]; 91 | unsigned char tag_bytes[48]; 92 | rpad_chars(extended_tag, tx->obsoleteTag, NUM_HASH_TRYTES); 93 | chars_to_bytes(extended_tag, tag_bytes, NUM_HASH_TRYTES); 94 | 95 | bytes_add_u32_mem(tag_bytes, tag_increment); 96 | bytes_to_chars(tag_bytes, extended_tag, 48); 97 | 98 | // TODO: do we need to increment both? Probably only obsoleteTag... 99 | memcpy(tx->obsoleteTag, extended_tag, 27); 100 | memcpy(tx->tag, extended_tag, 27); 101 | } 102 | 103 | static void set_bundle_hash(const BUNDLE_CTX *bundle_ctx, TX_OBJECT *txs, 104 | unsigned int num_txs) 105 | { 106 | char bundle[81]; 107 | bytes_to_chars(bundle_get_hash(bundle_ctx), bundle, 48); 108 | 109 | for (unsigned int i = 0; i < num_txs; i++) { 110 | memcpy(txs[i].bundle, bundle, 81); 111 | } 112 | } 113 | 114 | void prepare_transfers(char *seed, uint8_t security, TX_OUTPUT *outputs, 115 | int num_outputs, TX_INPUT *inputs, int num_inputs, 116 | char transaction_chars[][2673]) 117 | { 118 | // TODO use a proper timestamp 119 | const uint32_t timestamp = 0; 120 | const unsigned int num_txs = num_outputs + num_inputs * security; 121 | const unsigned int last_tx_index = num_txs - 1; 122 | 123 | unsigned char seed_bytes[48]; 124 | chars_to_bytes(seed, seed_bytes, 81); 125 | 126 | // first create the transaction objects 127 | TX_OBJECT txs[num_txs]; 128 | 129 | int idx = 0; 130 | for (unsigned int i = 0; i < num_outputs; i++) { 131 | 132 | // initialize with defaults 133 | memcpy(&txs[idx], &DEFAULT_TX, sizeof(TX_OBJECT)); 134 | 135 | rpad_chars(txs[idx].signatureMessageFragment, outputs[i].message, 2187); 136 | memcpy(txs[idx].address, outputs[i].address, 81); 137 | txs[idx].value = outputs[i].value; 138 | rpad_chars(txs[idx].obsoleteTag, outputs[i].tag, 27); 139 | txs[idx].timestamp = timestamp; 140 | txs[idx].currentIndex = idx; 141 | txs[idx].lastIndex = last_tx_index; 142 | rpad_chars(txs[idx].tag, outputs[i].tag, 27); 143 | idx++; 144 | } 145 | 146 | for (unsigned int i = 0; i < num_inputs; i++) { 147 | 148 | // initialize with defaults 149 | memcpy(&txs[idx], &DEFAULT_TX, sizeof(TX_OBJECT)); 150 | 151 | char *address = txs[idx].address; 152 | get_address(seed_bytes, inputs[i].key_index, security, address); 153 | txs[idx].value = -inputs[i].balance; 154 | txs[idx].timestamp = timestamp; 155 | txs[idx].currentIndex = idx; 156 | txs[idx].lastIndex = last_tx_index; 157 | idx++; 158 | 159 | // add meta transactions 160 | for (unsigned int j = 1; j < security; j++) { 161 | 162 | // initialize with defaults 163 | memcpy(&txs[idx], &DEFAULT_TX, sizeof(TX_OBJECT)); 164 | 165 | memcpy(txs[idx].address, address, 81); 166 | txs[idx].value = 0; 167 | txs[idx].timestamp = timestamp; 168 | txs[idx].currentIndex = idx; 169 | txs[idx].lastIndex = last_tx_index; 170 | idx++; 171 | } 172 | } 173 | 174 | // create a secure bundle 175 | BUNDLE_CTX bundle_ctx; 176 | bundle_initialize(&bundle_ctx, last_tx_index); 177 | 178 | for (unsigned int i = 0; i < num_txs; i++) { 179 | bundle_set_external_address(&bundle_ctx, txs[i].address); 180 | bundle_add_tx(&bundle_ctx, txs[i].value, txs[i].tag, txs[i].timestamp); 181 | } 182 | 183 | uint32_t tag_increment = bundle_finalize(&bundle_ctx); 184 | 185 | // increment the tag in the first transaction object 186 | increment_obsolete_tag(tag_increment, &txs[0]); 187 | 188 | // set the bundle hash in all transaction objects 189 | set_bundle_hash(&bundle_ctx, txs, num_txs); 190 | 191 | // sign the inputs 192 | tryte_t normalized_bundle_hash[81]; 193 | bundle_get_normalized_hash(&bundle_ctx, normalized_bundle_hash); 194 | 195 | for (unsigned int i = 0; i < num_inputs; i++) { 196 | SIGNING_CTX signing_ctx; 197 | signing_initialize(&signing_ctx, seed_bytes, inputs[i].key_index, 198 | security, normalized_bundle_hash); 199 | unsigned int idx = num_outputs + i * security; 200 | 201 | // exactly one fragment for transaction including meta transactions 202 | for (unsigned int j = 0; j < security; j++) { 203 | 204 | unsigned char signature_bytes[27 * 48]; 205 | signing_next_fragment(&signing_ctx, signature_bytes); 206 | bytes_to_chars(signature_bytes, txs[idx++].signatureMessageFragment, 207 | 27 * 48); 208 | } 209 | } 210 | 211 | // convert everything into trytes 212 | for (unsigned int i = 0; i < num_txs; i++) { 213 | get_transaction_chars(txs[i], transaction_chars[last_tx_index - i]); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /tests/test_vectors.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_VECTORS_H 2 | #define TEST_VECTORS_H 3 | 4 | #include "iota/iota_types.h" 5 | #include "api.h" 6 | 7 | #define MAX_ADDRESS_INDEX 4 8 | 9 | typedef struct TEST_VECTOR { 10 | 11 | char seed[NUM_HASH_TRYTES + 1]; 12 | char addresses[MAX_SECURITY_LEVEL + 1][MAX_ADDRESS_INDEX + 1][NUM_HASH_TRYTES + 1]; 13 | 14 | TX_INPUT bundle[8]; 15 | 16 | char bundle_hash[81]; 17 | 18 | char signature[2][4374]; 19 | 20 | } TEST_VECTOR; 21 | 22 | static const TEST_VECTOR PETER_VECTOR = { 23 | // for purely coincidental reasons this seed was intialy used in the development 24 | "PETERPETERPETERPETERPETERPETERPETERPETERPETERPETERPETERPETERPETERPETERPETERPETERR", { 25 | { /* ignore index 0 as there is no security level 0 */}, 26 | {"WLRSPFNMBJRWS9DFXCGIROJCZCPJQG9PMOO9CUZNQXTLLQAYXGXT9LECGEQ9MQIWIBGQREFHULPOETHNZ", 27 | "UMDTJXHIFVYVCHXKZNMQWMDHNLVQNMJMRULXUFRLNFVVUMKYZOAETVQOWSDUAKTXVNDSVAJCASTRQNV9D", 28 | "LHWIEGUADQXNMRKQSBDJOAFMBIFKHHZXYEFOU9WFRMBGODSNJAPGFHOUOSGDICSFVA9KOUPPCMLAHPHAW", 29 | "GDTLKEWSSLKLQYF9UYSFM9XOVWZYMPMCQOCJMCYJFEESUHBAFPCLNGOLMDHZSXX9WSSFUNDORMGADKIEA", 30 | "DJJTBISBQNSJTYYVRRXFQVTGHTNGOEJSVOXIJKW9NBHOZBZIUASYVI9FA9YYR9KVNQP9OLLUFGSZAZDDA"}, 31 | {"GUIOZDLUNXIGC9DCV9ZIEDBWRHHPILAYOYRVPTFPRAUZWLWDIXBSPCZGENHWDFHMQGCTOKMXITVVDMEFB", 32 | "MTPYSBLSL9HENRQKP9IPYYZTHEOECLXGYMZIYYUCYAPZYFAECX9ZSFOSFMDNYQAPYHVMTVUX9HNNUKOB9", 33 | "RKPTFXPROTSKXBKXLNSLOPOQGWASCLAECQQRWOKCJPNYHIFBUJXE9GHQJPIZHKYXXHC9BZJPHAROKBGSD", 34 | "JYJFIYFNTDPTPGSJWAKUFK9OLTISGIKSQPTLIVRVHLHRRCSJCEFQRTGWVTBUQFXHFRZICMFDTPDKNKDFW", 35 | "WPF9CTKYVMEWXHXL9NKR9XON9TPBP9UNM9FPWBUISVSHNULLVHSU9PMBNNR9FSZUPCNBXGJWLGRKKSLHW"}, 36 | {"GL9YTIZWBXCPSCBRAVAUBMNNCHIHZWABOYQ9NBXOMZCNCCZPQWTMRBKKJDZWUIWRUXHZVEXBCGYBMEMQX", 37 | "PROKBRGUUTYILP9KB9QVTXDODVRRWHP9IITVHYCYHWRDZFLIPRVARUXWURXDTUWNPWDFGTNSLXYUTWQTW", 38 | "AYVJGXBZOGIKYOCSDAMFNBZVSBKEVB9YNYD9EWONVIYPPYKWKWYXPBZSBEIZTRBZ9SDXYRIGWOERSSRDA", 39 | "PDBLCSZPTJTAVBBBHOYKVHETZG9RTLUIHAIPWJ9VNYPNXLYNCTCIIECH9OJHXOSGCORBR9OJCMCUQWWUX", 40 | "FDEBHWMDYRZCMJULJRUDTUCNCYMHJBYGUOTSIKQUANCY9YMYKAWKFNIWOUWOKYQLTZOIVXRITMJTNRMB9"} 41 | }, 42 | {{"ADRADRADRADRADRADRADRADRADRADRADRADRADRADRADRADRADRADRADRADRADRADRADRADRADRADRADR", 0, 10, "XC", 0, 2, 99999}, 43 | {"MTPYSBLSL9HENRQKP9IPYYZTHEOECLXGYMZIYYUCYAPZYFAECX9ZSFOSFMDNYQAPYHVMTVUX9HNNUKOB9", 1, -10, "", 1, 2, 99999}, 44 | {"MTPYSBLSL9HENRQKP9IPYYZTHEOECLXGYMZIYYUCYAPZYFAECX9ZSFOSFMDNYQAPYHVMTVUX9HNNUKOB9", 1, 0, "", 2, 2, 99999}}, 45 | "9WIOYSDBCGZNERIROVLDAJRJKFBNZHLXNXWGJXCXJJHU9GZWIJCTCRLLVGNLFQOERBZZFLYZYFQZDTCCD", 46 | 47 | {"WDVPPZTVCSGGLVAORJSZAILMLSINZUJUUPA9WWIC9OFBUJIIIDLSQYWK9KHBRMCTNEYSEHFJEJMZJBEFXHAUQUSL9UFTAQVPHNZXYIAEY9TFSBWQNCOPJKCYGQBINAU9SEJWGCUFOYMBGEZMGXWBGRVQQIZRHTLCSXIODLBNWDE9RWNIOYVJBPHRQAMTVZNFABTOXVGHDOCRGFDTCSNJCUTMBMBTBIBJAPUMTNUOORVOKZHSWKWUIW9PCVAAOANSCNEWFYQOOJMXNJJGJZCQGOYPRHHCZ9HEOTOTSNUZCGIBVQOBTZRUXUGC9HG9LCNLRP9AQXWEDFQLHHWIEWTG9MECPTCCCJWVSVVWDNMLTBHG9MCFTUPFHJ9WFBBPJOMJIKBNBFSSLHOD9UMVAO9FWSXPCJHKEDUTGUTXLXLIRZG9IRBEUJTTTWBTTKBHFPPRJDOJMAAPGLWWEAIBRXWHITLUAPVHQZW9WNGFRWXBIFEHJJERNIS9QXMQ9UXQECJMHAKMPDSGLLINDDIQAOQRJWMYBSNDVOTDMVFSIOADXPWYGNRLCUVUTWDWBTLMVJHSK9VFUQLWWXQJNSVSEPDWNOHNFFUKUFQBGDJLPDQVYTKDPUBMVGWGVPU99LUUYFQORROPTJX9CBAOM9YMUBPEGANCFSEUHEIIVYWYMYIIDCNGRZLPSOXATBPRIW9JAIMYYEIOUVKXILAUNGAKTWRWCS9MYRCOBIWTNWPOWDKNVLQUEWHCPMQQVBZJWQKLHBYQUNDCMFPWPB9CDDKUIIDSFDMYMUVHYZZNGOTUUAFKMBVW9HMWTSLY9MVRGWAITHZQTE9KZKAWGBOUGDGNZBXHFXY9HQXIQGXFYAUBZVBUIBIZLXLWGLXGPWUR9JCMTZYA9NLDBXSCKYEGJVGJK9KQE9AVFSBZGMOWPWTWHNYSIXRGUTWUYSGCFBTSTNVPEJOAXBFUENOTGNHCKGIKROSXJWIJN9OXLQWZESQXAZDZRIQUGYYASWHEEW9VJADFI9KMMXO9SWRETBIPFGHRUZNVYXMCBDFJWVIQKBVZONICDROJBGJE9LC9YBOYXQRQPEBVVIFUHXGXBIGGQPQCKBNUSKHRRDTZETTWOVZXGDIXKOQGOYTYHBG9JHTJ9EEEUOQOEFIAWZKJRPZBYSUBEXMFXGSWUEIBDJTFRZZZWGDFWCZFMBDBMHNWYEDPNWWBCNWPANETVLYFXHLVEW9VEXAMJNG9MQRGNC9ZVUBYWSIPXVTKWPUDMDRFCHAQBKDEUC9JRGCKTZSXIUFAXDADYWQEPIVFCCENUGUPTPCLBPMHOE9OF99QQ9AKOWX9YPEPIDFMQFJYHGUDZANRYLXDBSNSGCSQVJAFKSERBNDQGDSQVPMYCNMXFGJGEOCTIGJYSAJAGLQIIRDVVLAEVLZTTIHGPNJFDHGEWCSDWGSRDXS9PHPGYUKTCWGDRSVHBQZGLMNOXASBYORWIGJFUX9TPAQSSMBYFX9VJYRGY9CIAENTRJCMJWA9DIFBEDAGMXCEFSGB9MASXRSQBDIYRCCNKFGNIPFPDSEXHLSPERPANNZSCUQOWBEMRJI9NWPVWZHAYFBWAZUPNSKVYGLCMKSFSDGCW9LXXUCYIA9QVZDBGKHXFEZVPOAVAVNXEOZDRR9C9O9QSZHGJLHEGJQNVZNLYOMOPQGXXT9QBDF9P9KCBTN9XFXAHWAWOBP9SLTHYOSFTOZYKKWCOHBFAMOS9YARDEITFFPQQMIWZUVUTAFCDBDDWBJAMBK9LOHNTWQLNUAADTTSDJHLQBPFTAUTEPJPAQIHNKIQPJCXKIPOER9SQCEU9VMEQHAMNZDKWHTMHWTTPXPRSNUF9CCHRE9LICBJLDJESIJFV9RPOLMELJYTPLL9BLMLESQJRAJBTUJXCVHPPHVRITLNX9ZZRMTHM9TUGKSUGXO9PTRUOSNTPRFJIDNXMSMBAFJYTPBHHDVWMQLBTJJWRJDWWJHMKUNRGWHBGPLKCDZFUSOAIO9FZYWPDEDJXWZKJPERX9TEI9PEENFNQJYWMTTIZSYFTJZLCMZMDIYTEBCJOFJAKUZDKQZMQPZK9BL9RMXVOYCFYHDNFCHNGAYJDMKKQOPIRWEFJXSIELXJGECDCWDHECXZ9HWVFBKKAFHJUPUKF9MEZ9EGZHMKITJDQQOZBKJMEUDCCNETJ9OGGCTRTCJJHCDWLGDNHVRLR9IFMNZS9BHIZPRRCZIFWKCGDMTMFNPYYWBEYPVHQUCOSMBGJCDBGLZ9QYUPKRIDNBPKNUTQKGHVXJHLGI9YEMOOPNPZY9LEWZSXHFRUBVOJBRUE9JFRWNKADETPHMHAWGUT9KXHUQIQN9TPFYJFUXWUEEHZELVRRHMQYAPO9LZBSEPXJMOQYZC9TSZULXTKN9MGAINY9HWSUENNWDONMDGCKNRKNRNQEMLLSAWK9CGYELPNKOGCWFJAEQ9STPGJF9FADUDYRSMKTTMUVTXLAQDNAQZWNFRUFZXNM9WVWE9LDF9LYDQWCMCNEEUTTBWXKQHTMIQKMSDHAYERUCNRZCPNBZMGTETYYBYLPZD9ZQEGIXZWCWLTZVZDAFPUTULGGLLMOORNPPCWTVWOY9MZRXRUKBVZMJREQOJTKNBTEAJWCNPTGPGCWWPKMJFVGLSOEMEUJWWYAQCPPLRUCUMJWPQWHWVKY9TXHJ9HLJPHMNHNZONQFYAJMMKCVDFEWZKLLCCFHLJBYBWDONCLOH9POVSVAWRPGEWJOMTZFOWMRZHOZGGUKABHOVZVLWTZRTTIUCFQDYTHSEVDILVVOQBWQQZZYEYGBDYR9TIZZFE9IRDEJNYRUBAPXTFDHEVNZYVLVZ9TEUXMWNBYYOOFUCEI9AUFVBYLOWJDJWBOGDPTHUEIKSW9VTCCCYHANAGCBKXUCCRCPGDVNPMWVC9UJQU9OFHWSVQQWBSLYOMTIPBIQAJBJQTCEPACBQE9DFSFCJA9XXKLEASKCNNEWCHIKDOOWPFCMGHFJQWVZLLEFOEDGKMZQP9VL9VTSJDLGHPUTSKBOUIPIZVP9HICMOHLARYLRWRNYUKFJWDQKRTQQIPUOKBA9MRCIXUORUWEYSUWUZHGVYAJIZUNCJOESTVWEVLDXCWZNNQQITNDHZUQBNFEHDDUHRWXDENYAAWYJMXNFRJRPPGBKTHDHXYXKLFYP9RTYKKDZMRZQWZLZ9WQCILCSUNFBU9VNPAKFGJ9OPDHQSYCJAXTTJPLWL9GB9ZROLPTMAQZOLOOLVPISSOQCDAGSGYRTUYNJORAJ9AYM9MDXJPSTLXEKKKPFRXRUJHKBTUPHWHKMDUTZMDQDZILMAHVYJLBAXWDAMZBBT9VDFAYBKRVQZTMLSHCAXCLJQGDZE9QW9LQZGXGBJWLWBHWEFZJYYKSE9JJPYLJMESAQGWHCJANOEV9XVFSIISZRHHUVNJDDQN99QURZ9ZTJL9DNOBZVOYWLAPMNPWYETTXN9Q9ULKQVRYZSGXYNATKUIEMQINXPXZD9TCPRYAM9F99AFZVLIIE9Y9VQFGMROAMIPIDWIFQXTQDULMEJHRBXSLDPTZLXDEKKJDFZBTEGVSZMCNHUETBOMUUIKZTHAOL9JEI9RIKNCOFLMRUXTWQU9XOGNSVAAFZYSERUFALFINZIVBCGLYXPNCMUZEQPAL9WSHDFDRKDJISVMFSYQWLWLASOVTGYPRUZLNDCYUAYJHAFBOFQFQDNYTFLZDHD9TFKKLITUYBOKZMATPB9DIRLUYKIDXUCFRNDXK9MILALRLRDIHG9EIQQBOFIVDEGFZKKKIDVTZCYARVPHZBPJF99RHZLUORDTGXEWIDNSOKVSAHWOCGVJH9PZYMRYIAEQCMJZYFUO9BDUPOQJUYFSQ9JXRNWOFGOLPBYHIDHYQRZDMQCYCJMKHMRCEQUVKHYFRQZADRWXPEXDKGVITABYGQQOMJWN9ECKVA9VEWHTGLKCMHHEYDYXOHXHQZXLSZUFRBVTMAVRPSTUXAXQSU9LZKAOYCFVOI9N9LKDIDJOGKFNNODVEAWYPQTWPTSQZNBUJSZERWGGZDZCBFLZWVFIGBVKSVWXSBGMTURZRGFYJFLWZSJZIVNBZEYHSDRBKNYOXPMHDCSGSRCHA9DLGMKWQC9ZMVZMM9RCJZPXMLCSTELCM9MYALANFTRLZZXKH9UPXDJVCJOQVKOTYZGKUPTRFBHGZVSLJAV9JAHFLIAHXLAZH9DDPBLWRUAXXFJKRVSQSVABUJPAIRWBPOD9NNFJACUCWRYVKKUTCVMHGYWNYMSRAFXCEIITQJEIEGUEHHUYXKWQXFWQRVYG9UMRKJUEHODMKMDLBMMPTW9ZWGTJFGJNFUSBLTIQON9Y", ""} 48 | }; 49 | 50 | static const TEST_VECTOR OVERFLOW_VECTOR = { 51 | "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM", { 52 | { /* ignore index 0 as there is no security level 0 */}, 53 | {"SDJGHWUKBMMDMLBVPARNPJZSXJLE9IVWLMXNLGBAGW9BVUWCLBZFIUWCHMUKAO9NOEBXTWLPTGNOFLUUD", 54 | "EZCPFKWUFUPNNTLPOSQUF9ARBWOXTACYDEWGOHGNZWPMOBMRHYTXOYBISQASOUCUIBHEJAKVBVEVIPFNY", 55 | "RGXSOSXNPSECZJZBOWSPQV9FEHAECO9PLVR9DRFCOZOUYT9OZMZOGIPIIIXARBWOHAYUDFFUFTROAUBNB", 56 | "DR9TTRXS9CQVBSEATLURTEVNCZWPNFTRIHYZENBXXTHZLIOGPVMFFHCGK9POSHKE9BFAYQQN9NMKA99GC", 57 | "WVCBEC9DLNWIJKIQIXWHHKYUMXOT9J9WAMZTIVLDOSKHGLVGYTAEOVOQQNSV9DLVU9ENFXGCPUGAHLIJY"}, 58 | {"VSSIPYVZYLPSMIB9HFPIM9ONASKJHETXRMJNIBRBZNJRWIMD9WVITVUC9FZHIZHGLBKAY9HAPGIZVQOQA", 59 | "MDWYEJJHJDIUVPKDY9EACGDJUOP9TLYDWETUBOYCBLYXYYYJYUXYUTCTPTDGJYFKMQMCNZDQPTBE9AFIW", 60 | "BRCRVAASDLAZPTSHELUSJGNEWQSCLY9WHEARHXSJBQFNSMTES9OQULMXNNLWSZDE9K9HOWQHPMTVNHEMD", 61 | "BVTCAAJ9KVBYCDXUATNBFOIOVALZZJCVEMWSWHHKBLCQ9BXRFZPN9ER9WXUROWIJVRWREWJNAWTOGH9OW", 62 | "BPBX9PPTMYXBYSELTKUJVROKMFLCSQMCGKMMYMXSFPQNDWRQ9RWJBEEERFAO9ZHWGTKTWEBMCDBRUUAHC"}, 63 | {"RBUXEKPRWLYEPINEWH9AIL9DUYRWBZHFQDNPQLXIUVTEYWTSGDIZOTWALPNIFIFJCWQDPFWPNTOFRQZHC", 64 | "UDVBLCHJDIPXJZKKCNVLRLMCPHXKGZQGURCE9M9RQLWPBTSFSVFBJWSMNEUEMLHGOWS9ZXFAWFDXWEEBW", 65 | "OHHNETEIFRKUUQFLKXBPQLFCFWYQ9GLJWLFWCZNWDD9WNTHNWRVUSPNGPHWVAZLWUBCZXMSPB99DEYPSD", 66 | "NIKLXGWY9VY9MLVZOTENPTQFBUWOUZYTPUUBQRHQ9HGFJNUVRANV9CCOTORJPTAKP9UBQDGENB9BWIMXW", 67 | "MZDWEIFYLRYNBNBZFZIIGXKIUDVDBSCPYXKJKIVKK9AUWSLZLTHGIRBFKUJQMGGTWGNPBYLNB9CUSLDKW"} 68 | }, {}, "", {} 69 | }; 70 | 71 | 72 | #endif // TEST_VECTORS_H 73 | -------------------------------------------------------------------------------- /tests/bundle_test.c: -------------------------------------------------------------------------------- 1 | #include "test_common.h" 2 | #include "iota/conversion.h" 3 | // include the c-file to be able to test static functions 4 | #include "iota/bundle.c" 5 | 6 | static void test_increment_tag(void **state) 7 | { 8 | UNUSED(state); 9 | 10 | const int64_t value = -1; 11 | const uint32_t timestamp = 0; 12 | const uint32_t current_index = 0; 13 | const uint32_t last_index = 1; 14 | char tag[] = "999999999999999999999999999"; 15 | 16 | unsigned char bytes[NUM_HASH_BYTES]; 17 | create_bundle_bytes(value, tag, timestamp, current_index, last_index, 18 | bytes); 19 | bytes_increment_trit_area_81(bytes); 20 | 21 | // incrementing the 82nd trit should be equivalent to incrementing the tag 22 | tag[0] = 'A'; 23 | 24 | unsigned char exp_bytes[NUM_HASH_BYTES]; 25 | create_bundle_bytes(value, tag, timestamp, current_index, last_index, 26 | exp_bytes); 27 | 28 | assert_memory_equal(bytes, exp_bytes, NUM_HASH_BYTES); 29 | } 30 | 31 | static void test_normalize_hash(void **state) 32 | { 33 | UNUSED(state); 34 | 35 | tryte_t hash_trytes[NUM_HASH_TRYTES] = {0}; 36 | 37 | // test_hash is randomly generated using KeePass. Not an actual hash (but 38 | // shoudln't matter). 39 | char test_hash[] = "IGSCBIJOFWHFLSXJV9ZENNTNWTEWGFMZKT9UBWRHVOJRLULQELXWS9Z" 40 | "HPGVBUTAVMCPHIMRWSHWMSYAKP"; 41 | chars_to_trytes(test_hash, hash_trytes, NUM_HASH_TRYTES); 42 | normalize_hash(hash_trytes); 43 | 44 | // exp_trytes is taken directly from iota.lib.js 45 | tryte_t exp_trytes[NUM_HASH_TRYTES] = { 46 | 13, 9, -8, 3, 2, 9, 10, -12, 6, -4, 8, 6, 12, -8, 47 | -3, 10, -5, 0, -1, 5, -13, -13, -7, -13, -4, -7, 5, -13, 48 | 6, 6, 13, -1, 11, -7, 0, -6, 2, -4, -9, 8, -5, -12, 49 | 10, -9, 12, -6, 12, -10, 5, 12, -3, -4, -8, 0, -6, 8, 50 | -11, 7, -5, 2, -6, -7, 1, -5, 13, 3, -11, 8, 9, 13, 51 | -9, -4, -8, 8, -4, 13, -8, -2, 1, 11, -11}; 52 | assert_memory_equal(hash_trytes, exp_trytes, NUM_HASH_TRYTES); 53 | } 54 | 55 | static void test_normalize_hash_zero(void **state) 56 | { 57 | UNUSED(state); 58 | 59 | tryte_t hash_trytes[NUM_HASH_TRYTES] = {0}; 60 | normalize_hash(hash_trytes); 61 | 62 | // all zero hash is already normalized 63 | static const tryte_t exp_trytes[NUM_HASH_TRYTES] = {0}; 64 | assert_memory_equal(hash_trytes, exp_trytes, NUM_HASH_TRYTES); 65 | } 66 | 67 | static void test_normalize_hash_one(void **state) 68 | { 69 | UNUSED(state); 70 | 71 | tryte_t hash_trytes[NUM_HASH_TRYTES] = {MAX_TRYTE_VALUE, MAX_TRYTE_VALUE}; 72 | normalize_hash(hash_trytes); 73 | 74 | // in the normalized hash the first tryte will be reduced to lowest value 75 | static const tryte_t exp_trytes[NUM_HASH_TRYTES] = {MIN_TRYTE_VALUE, 76 | MAX_TRYTE_VALUE}; 77 | assert_memory_equal(hash_trytes, exp_trytes, NUM_HASH_TRYTES); 78 | } 79 | 80 | static void test_normalize_hash_neg_one(void **state) 81 | { 82 | UNUSED(state); 83 | 84 | tryte_t hash_trytes[NUM_HASH_TRYTES] = {MIN_TRYTE_VALUE, MIN_TRYTE_VALUE}; 85 | normalize_hash(hash_trytes); 86 | 87 | // in the normalized hash the first tryte will be reduced to highest value 88 | static const tryte_t exp_trytes[NUM_HASH_TRYTES] = {MAX_TRYTE_VALUE, 89 | MIN_TRYTE_VALUE}; 90 | assert_memory_equal(hash_trytes, exp_trytes, NUM_HASH_TRYTES); 91 | } 92 | 93 | // Hash relevant content of one transaction 94 | typedef struct TX_ENTRY { 95 | const char *address; 96 | const int64_t value; 97 | const char *tag; 98 | const uint32_t timestamp; 99 | } TX_ENTRY; 100 | 101 | static void construct_bundle(const TX_ENTRY *txs, unsigned int num_txs, 102 | BUNDLE_CTX *bundle_ctx) 103 | { 104 | bundle_initialize(bundle_ctx, num_txs - 1); 105 | 106 | for (unsigned int i = 0; i < num_txs; i++) { 107 | assert_int_equal(strlen(txs[i].address), 81); 108 | bundle_set_external_address(bundle_ctx, txs[i].address); 109 | 110 | assert_int_equal(strlen(txs[i].tag), 27); 111 | bundle_add_tx(bundle_ctx, txs[i].value, txs[i].tag, txs[i].timestamp); 112 | } 113 | } 114 | 115 | static void test_empty_bundle(void **state) 116 | { 117 | UNUSED(state); 118 | 119 | BUNDLE_CTX bundle_ctx; 120 | expect_assert_failure(construct_bundle(NULL, 0, &bundle_ctx)); 121 | } 122 | 123 | static void test_one_tx_bundle(void **state) 124 | { 125 | UNUSED(state); 126 | 127 | const TX_ENTRY txs[] = { 128 | {"LHWIEGUADQXNMRKQSBDJOAFMBIFKHHZXYEFOU9WFRMBGODSNJAPGFHOUOSGDICSFVA9K" 129 | "OUPPCMLAHPHAW", 130 | 10, "999999999999999999999999999", 0}}; 131 | 132 | BUNDLE_CTX bundle_ctx; 133 | expect_assert_failure( 134 | construct_bundle(txs, sizeof(txs) / sizeof(TX_ENTRY), &bundle_ctx)); 135 | } 136 | 137 | static void test_bundle_hash(void **state) 138 | { 139 | UNUSED(state); 140 | 141 | const TX_ENTRY txs[] = { 142 | {"LHWIEGUADQXNMRKQSBDJOAFMBIFKHHZXYEFOU9WFRMBGODSNJAPGFHOUOSGDICSFVA9K" 143 | "OUPPCMLAHPHAW", 144 | 10, "ZOA999999999999999999999999", 0}, 145 | {"WLRSPFNMBJRWS9DFXCGIROJCZCPJQG9PMOO9CUZNQXTLLQAYXGXT9LECGEQ9MQIWIBGQ" 146 | "REFHULPOETHNZ", 147 | -5, "999999999999999999999999999", 0}, 148 | {"UMDTJXHIFVYVCHXKZNMQWMDHNLVQNMJMRULXUFRLNFVVUMKYZOAETVQOWSDUAKTXVNDS" 149 | "VAJCASTRQNV9D", 150 | -5, "999999999999999999999999999", 0}}; 151 | const char exp_hash[] = "VMSEGGHKOUYTE9JNZEQIZWFUYHATWEVXAIJNPG9EDPCQRFAFWP" 152 | "CVGHYJDJWXAFNWRGUUPULXOCEJDBUVD"; 153 | 154 | BUNDLE_CTX bundle_ctx; 155 | construct_bundle(txs, sizeof(txs) / sizeof(TX_ENTRY), &bundle_ctx); 156 | 157 | compute_hash(&bundle_ctx); 158 | 159 | char hash_chars[NUM_HASH_TRYTES + 1]; 160 | bytes_to_chars(bundle_get_hash(&bundle_ctx), hash_chars, NUM_HASH_BYTES); 161 | // make null-terminated 162 | hash_chars[NUM_HASH_TRYTES] = '\0'; 163 | 164 | assert_string_equal(hash_chars, exp_hash); 165 | } 166 | 167 | static void test_bundle_finalize(void **state) 168 | { 169 | UNUSED(state); 170 | 171 | const TX_ENTRY txs[] = { 172 | {"LHWIEGUADQXNMRKQSBDJOAFMBIFKHHZXYEFOU9WFRMBGODSNJAPGFHOUOSGDICSFVA9K" 173 | "OUPPCMLAHPHAW", 174 | 10, "999999999999999999999999999", 0}, 175 | {"WLRSPFNMBJRWS9DFXCGIROJCZCPJQG9PMOO9CUZNQXTLLQAYXGXT9LECGEQ9MQIWIBGQ" 176 | "REFHULPOETHNZ", 177 | -5, "999999999999999999999999999", 0}, 178 | {"UMDTJXHIFVYVCHXKZNMQWMDHNLVQNMJMRULXUFRLNFVVUMKYZOAETVQOWSDUAKTXVNDS" 179 | "VAJCASTRQNV9D", 180 | -5, "999999999999999999999999999", 0}}; 181 | const char exp_hash[] = "VMSEGGHKOUYTE9JNZEQIZWFUYHATWEVXAIJNPG9EDPCQRFAFWP" 182 | "CVGHYJDJWXAFNWRGUUPULXOCEJDBUVD"; 183 | const unsigned int exp_tag_increment = 404; 184 | 185 | BUNDLE_CTX bundle_ctx; 186 | construct_bundle(txs, sizeof(txs) / sizeof(TX_ENTRY), &bundle_ctx); 187 | 188 | const uint32_t tag_increment = bundle_finalize(&bundle_ctx); 189 | assert_int_equal(tag_increment, exp_tag_increment); 190 | 191 | char hash_chars[NUM_HASH_TRYTES + 1]; 192 | bytes_to_chars(bundle_get_hash(&bundle_ctx), hash_chars, NUM_HASH_BYTES); 193 | // make null-terminated 194 | hash_chars[NUM_HASH_TRYTES] = '\0'; 195 | 196 | assert_string_equal(hash_chars, exp_hash); 197 | } 198 | 199 | static void test_max_value_txs_bundle_finalize(void **state) 200 | { 201 | UNUSED(state); 202 | 203 | const TX_ENTRY txs[] = { 204 | {"UMDTJXHIFVYVCHXKZNMQWMDHNLVQNMJMRULXUFRLNFVVUMKYZOAETVQOWSDUAKTXVNDSV" 205 | "AJCASTRQNV9D", 206 | MAX_IOTA_VALUE, "MMMMMMMMMMMMMMMMMMMMMMMMMMM", 0xFFFFFFFF}, 207 | {"WLRSPFNMBJRWS9DFXCGIROJCZCPJQG9PMOO9CUZNQXTLLQAYXGXT9LECGEQ9MQIWIBGQR" 208 | "EFHULPOETHNZ", 209 | -MAX_IOTA_VALUE, "MMMMMMMMMMMMMMMMMMMMMMMMMMM", 0xFFFFFFFF}}; 210 | const char exp_hash[] = "9ZARQDSKQGVYEKJGVILRTTLBGCTYITLIYBDBGSFDUKWINXSHCP" 211 | "AWNXSCIPVVDDFWYEHQITKGOUYGYAPRD"; 212 | const unsigned int exp_tag_increment = 79; 213 | 214 | BUNDLE_CTX bundle_ctx; 215 | construct_bundle(txs, sizeof(txs) / sizeof(TX_ENTRY), &bundle_ctx); 216 | 217 | const uint32_t tag_increment = bundle_finalize(&bundle_ctx); 218 | assert_int_equal(tag_increment, exp_tag_increment); 219 | 220 | char hash_chars[NUM_HASH_TRYTES + 1]; 221 | bytes_to_chars(bundle_get_hash(&bundle_ctx), hash_chars, NUM_HASH_BYTES); 222 | // make null-terminated 223 | hash_chars[NUM_HASH_TRYTES] = '\0'; 224 | 225 | assert_string_equal(hash_chars, exp_hash); 226 | } 227 | 228 | int main(void) 229 | { 230 | const struct CMUnitTest tests[] = { 231 | cmocka_unit_test(test_increment_tag), 232 | cmocka_unit_test(test_normalize_hash), 233 | cmocka_unit_test(test_normalize_hash_zero), 234 | cmocka_unit_test(test_normalize_hash_one), 235 | cmocka_unit_test(test_normalize_hash_neg_one), 236 | cmocka_unit_test(test_empty_bundle), 237 | cmocka_unit_test(test_one_tx_bundle), 238 | cmocka_unit_test(test_bundle_hash), 239 | cmocka_unit_test(test_bundle_finalize), 240 | cmocka_unit_test(test_max_value_txs_bundle_finalize)}; 241 | 242 | return cmocka_run_group_tests(tests, NULL, NULL); 243 | } 244 | -------------------------------------------------------------------------------- /src/iota/bundle.c: -------------------------------------------------------------------------------- 1 | #include "bundle.h" 2 | #include 3 | #include "common.h" 4 | #include "addresses.h" 5 | #include "conversion.h" 6 | #include "kerl.h" 7 | 8 | // pointer to the first byte of the current transaction 9 | #define TX_BYTES(C) ((C)->bytes + (C)->current_index * 96) 10 | 11 | void bundle_initialize(BUNDLE_CTX *ctx, uint32_t last_index) 12 | { 13 | if (last_index >= MAX_BUNDLE_INDEX_SZ) { 14 | THROW(INVALID_PARAMETER); 15 | } 16 | 17 | os_memset(ctx, 0, sizeof(BUNDLE_CTX)); 18 | ctx->last_index = last_index; 19 | } 20 | 21 | void bundle_set_external_address(BUNDLE_CTX *ctx, const char *address) 22 | { 23 | if (!bundle_has_open_txs(ctx)) { 24 | THROW(INVALID_STATE); 25 | } 26 | 27 | unsigned char *bytes_ptr = TX_BYTES(ctx); 28 | chars_to_bytes(address, bytes_ptr, 81); 29 | } 30 | 31 | void bundle_set_internal_address(BUNDLE_CTX *ctx, const char *address, 32 | uint32_t index) 33 | { 34 | bundle_set_external_address(ctx, address); 35 | ctx->indices[ctx->current_index] = index; 36 | } 37 | 38 | void bundle_set_address_bytes(BUNDLE_CTX *ctx, const unsigned char *addresses) 39 | { 40 | if (!bundle_has_open_txs(ctx)) { 41 | THROW(INVALID_STATE); 42 | } 43 | 44 | unsigned char *bytes_ptr = TX_BYTES(ctx); 45 | os_memcpy(bytes_ptr, addresses, 48); 46 | } 47 | 48 | static void create_bundle_bytes(int64_t value, const char *tag, 49 | uint32_t timestamp, uint32_t current_index, 50 | uint32_t last_index, unsigned char *bytes) 51 | { 52 | trit_t bundle_essence_trits[243] = {0}; 53 | 54 | int64_to_trits(value, bundle_essence_trits, 81); 55 | chars_to_trits(tag, bundle_essence_trits + 81, 27); 56 | int64_to_trits(timestamp, bundle_essence_trits + 162, 27); 57 | int64_to_trits(current_index, bundle_essence_trits + 189, 27); 58 | int64_to_trits(last_index, bundle_essence_trits + 216, 27); 59 | 60 | // now we have exactly one chunk of 243 trits 61 | trits_to_bytes(bundle_essence_trits, bytes); 62 | } 63 | 64 | uint32_t bundle_add_tx(BUNDLE_CTX *ctx, int64_t value, const char *tag, 65 | uint32_t timestamp) 66 | { 67 | if (!bundle_has_open_txs(ctx)) { 68 | THROW(INVALID_STATE); 69 | } 70 | 71 | unsigned char *bytes_ptr = TX_BYTES(ctx); 72 | 73 | // the combined trits make up the second part 74 | create_bundle_bytes(value, tag, timestamp, ctx->current_index, 75 | ctx->last_index, bytes_ptr + 48); 76 | 77 | // store the binary value 78 | ctx->values[ctx->current_index] = value; 79 | 80 | return ctx->current_index++; 81 | } 82 | 83 | static inline int decrement_tryte(int max, tryte_t *tryte) 84 | { 85 | const int slack = *tryte - MIN_TRYTE_VALUE; 86 | if (slack <= 0) { 87 | return 0; 88 | } 89 | 90 | const int dec = MIN(max, slack); 91 | *tryte -= dec; 92 | 93 | return dec; 94 | } 95 | 96 | static inline int increment_tryte(int max, tryte_t *tryte) 97 | { 98 | const int slack = MAX_TRYTE_VALUE - *tryte; 99 | if (slack <= 0) { 100 | return 0; 101 | } 102 | 103 | const int inc = MIN(max, slack); 104 | *tryte += inc; 105 | 106 | return inc; 107 | } 108 | 109 | static void normalize_hash_fragment(tryte_t *fragment_trytes) 110 | { 111 | int sum = 0; 112 | for (unsigned int j = 0; j < 27; j++) { 113 | sum += fragment_trytes[j]; 114 | } 115 | 116 | for (unsigned int j = 0; j < 27; j++) { 117 | if (sum > 0) { 118 | sum -= decrement_tryte(sum, &fragment_trytes[j]); 119 | } 120 | else if (sum < 0) { 121 | sum += increment_tryte(-sum, &fragment_trytes[j]); 122 | } 123 | if (sum == 0) { 124 | break; 125 | } 126 | } 127 | } 128 | 129 | static inline void normalize_hash(tryte_t *hash_trytes) 130 | { 131 | for (unsigned int i = 0; i < 3; i++) { 132 | normalize_hash_fragment(hash_trytes + i * 27); 133 | } 134 | } 135 | 136 | void normalize_hash_bytes(const unsigned char *hash_bytes, 137 | tryte_t *normalized_hash_trytes) 138 | { 139 | bytes_to_trytes(hash_bytes, normalized_hash_trytes); 140 | normalize_hash(normalized_hash_trytes); 141 | } 142 | 143 | static bool validate_address(const unsigned char *addr_bytes, 144 | const unsigned char *seed_bytes, uint32_t idx, 145 | unsigned int security) 146 | { 147 | unsigned char computed_addr[48]; 148 | get_public_addr(seed_bytes, idx, security, computed_addr); 149 | 150 | return (memcmp(addr_bytes, computed_addr, 48) == 0); 151 | } 152 | 153 | static bool validate_balance(const BUNDLE_CTX *ctx) 154 | { 155 | int64_t balance = 0; 156 | for (unsigned int i = 0; i <= ctx->last_index; i++) { 157 | balance += ctx->values[i]; 158 | } 159 | 160 | return balance == 0; 161 | } 162 | 163 | /** @brief Checks that every input transaction has meta transactions. */ 164 | static bool validate_meta_txs(const BUNDLE_CTX *ctx, unsigned int security) 165 | { 166 | for (unsigned int i = 0; i <= ctx->last_index; i++) { 167 | if (ctx->values[i] < 0) { 168 | const unsigned char *input_addr_bytes = 169 | bundle_get_address_bytes(ctx, i); 170 | 171 | for (unsigned int j = 1; j < security; j++) { 172 | if (i + j > ctx->last_index || ctx->values[i + j] != 0) { 173 | return false; 174 | } 175 | if (memcmp(input_addr_bytes, 176 | bundle_get_address_bytes(ctx, i + j), 177 | NUM_HASH_BYTES) != 0) { 178 | return false; 179 | } 180 | } 181 | } 182 | } 183 | 184 | return true; 185 | } 186 | 187 | static bool validate_address_indices(const BUNDLE_CTX *ctx, 188 | unsigned int change_tx_index, 189 | const unsigned char *seed_bytes, 190 | unsigned int security) 191 | { 192 | for (unsigned int i = 0; i <= ctx->last_index; i++) { 193 | // only check the change and input addresses 194 | if (i == change_tx_index || ctx->values[i] < 0) { 195 | const unsigned char *addr_bytes = bundle_get_address_bytes(ctx, i); 196 | 197 | if (!validate_address(addr_bytes, seed_bytes, ctx->indices[i], 198 | security)) { 199 | return false; 200 | } 201 | } 202 | } 203 | 204 | return true; 205 | } 206 | 207 | static bool validate_address_reuse(const BUNDLE_CTX *ctx) 208 | { 209 | for (unsigned int i = 0; i <= ctx->last_index; i++) { 210 | 211 | if (ctx->values[i] == 0) { 212 | continue; 213 | } 214 | const unsigned char *addr_bytes = bundle_get_address_bytes(ctx, i); 215 | 216 | for (unsigned int j = i + 1; j <= ctx->last_index; j++) { 217 | if (ctx->values[j] != 0 && 218 | memcmp(addr_bytes, bundle_get_address_bytes(ctx, j), 219 | NUM_HASH_BYTES) == 0) { 220 | return false; 221 | } 222 | } 223 | } 224 | 225 | return true; 226 | } 227 | 228 | static bool validate_bundle(const BUNDLE_CTX *ctx, unsigned int change_tx_index, 229 | const unsigned char *seed_bytes, 230 | unsigned int security) 231 | { 232 | if (!validate_balance(ctx)) { 233 | return false; 234 | } 235 | 236 | if (!validate_meta_txs(ctx, security)) { 237 | return false; 238 | } 239 | 240 | if (!validate_address_indices(ctx, change_tx_index, seed_bytes, security)) { 241 | return false; 242 | } 243 | 244 | if (!validate_address_reuse(ctx)) { 245 | return false; 246 | } 247 | 248 | return true; 249 | } 250 | 251 | static void compute_hash(BUNDLE_CTX *ctx) 252 | { 253 | cx_sha3_t sha; 254 | 255 | kerl_initialize(&sha); 256 | kerl_absorb_bytes(&sha, ctx->bytes, TX_BYTES(ctx) - ctx->bytes); 257 | kerl_squeeze_final_chunk(&sha, ctx->hash); 258 | } 259 | 260 | static bool bundle_validate_hash(BUNDLE_CTX *ctx) 261 | { 262 | tryte_t hash_trytes[81]; 263 | compute_hash(ctx); 264 | normalize_hash_bytes(ctx->hash, hash_trytes); 265 | 266 | if (memchr(hash_trytes, MAX_TRYTE_VALUE, 81) != NULL) { 267 | // if the hash is invalid, reset it to zero 268 | os_memset(ctx->hash, 0, 48); 269 | return false; 270 | } 271 | 272 | return true; 273 | } 274 | 275 | bool bundle_validating_finalize(BUNDLE_CTX *ctx, uint32_t change_index, 276 | const unsigned char *seed_bytes, 277 | unsigned int security) 278 | { 279 | if (bundle_has_open_txs(ctx)) { 280 | THROW(INVALID_STATE); 281 | } 282 | 283 | return validate_bundle(ctx, change_index, seed_bytes, security) && 284 | bundle_validate_hash(ctx); 285 | } 286 | 287 | unsigned int bundle_finalize(BUNDLE_CTX *ctx) 288 | { 289 | unsigned int tag_increment = 0; 290 | 291 | if (bundle_has_open_txs(ctx)) { 292 | THROW(INVALID_STATE); 293 | } 294 | 295 | while (!bundle_validate_hash(ctx)) { 296 | // increment the tag of the first transaction 297 | bytes_increment_trit_area_81(ctx->bytes + 48); 298 | tag_increment++; 299 | } 300 | 301 | // the not normalized hash is already in the result pointer 302 | return tag_increment; 303 | } 304 | 305 | const unsigned char *bundle_get_address_bytes(const BUNDLE_CTX *ctx, 306 | uint32_t tx_index) 307 | { 308 | if (tx_index >= ctx->current_index) { 309 | THROW(INVALID_PARAMETER); 310 | } 311 | 312 | return ctx->bytes + tx_index * 96; 313 | } 314 | 315 | const unsigned char *bundle_get_hash(const BUNDLE_CTX *ctx) 316 | { 317 | if (bundle_has_open_txs(ctx)) { 318 | THROW(INVALID_STATE); 319 | } 320 | // TODO check that the bundle has already been finalized 321 | 322 | return ctx->hash; 323 | } 324 | 325 | void bundle_get_normalized_hash(const BUNDLE_CTX *ctx, tryte_t *hash_trytes) 326 | { 327 | bytes_to_trytes(bundle_get_hash(ctx), hash_trytes); 328 | normalize_hash(hash_trytes); 329 | } 330 | -------------------------------------------------------------------------------- /tests/signing_test.c: -------------------------------------------------------------------------------- 1 | #include "test_common.h" 2 | #include "iota/conversion.h" 3 | #include "iota/signing.h" 4 | #include "iota/bundle.c" 5 | 6 | #define MAX_SECURITY 3 7 | #define MAX_SIGNATURE_LENGTH ((MAX_SECURITY)*27 * (NUM_HASH_TRYTES)) 8 | 9 | // for purely coincidental reasons this seed was intialy used in the development 10 | static const char PETER_SEED[] = 11 | "PETERPETERPETERPETERPETERPETERPETERPETERPETERPETERPETERPETERPETERPETERPETE" 12 | "RPETERR"; 13 | static const uint32_t IDX = 0; 14 | 15 | static const tryte_t NORMALIZED_HASH[NUM_HASH_TRYTES] = {0}; 16 | 17 | static const char *FRAGMENTS[] = { 18 | "GTYOKCTTBPHTOTHZPNCNVFSFXGPSPUYFPUMSJKGJIBLLRBLKBEQEVXQT9QZWIKYDTYHZU9YOGX" 19 | "IMIGHJWZICUMVD9NDTLK9UWTWYIMAYNN9V9VPVUQPXZMFUTQZCFUNMONHDQYYFKUMIPJTGCZNC" 20 | "MHKDLHNEYFVZGAUO9LSSZNXIYPTIXZQFZSYONIVMLWRZRQHPJTYALCBHPKNTD9SZTBNQMZF9VN" 21 | "UDMMAYVGJCMQA9TCGX9ZDVOOQKZPDLVUI9V9EBNWIEWMTOZZPMAEFB9MWRPAKIKGZQAVTCQMSF" 22 | "IQSQWNF9RHOLEILBIIBKUF9W9VLXCTESUSBZWOWLTZRDGYBVZUWEFSHBGOVKYXDNWQTISFDBCG" 23 | "KHVWNVNLMUVPWGTPROYXVEGIVQGTDMNGXFAYAWALMJG9HXBQVMCAQHDXV9FYIMMXWJNQFPAJAB" 24 | "RGSBCBLRKUW9TRNRPX9QYTDOOYELJVMYPFBCTUBXYWMHSHBJCKXGPDISTD9PFCPUDKJUBPJ9OV" 25 | "QAEVHNUOENWPCLTK9ELFHZNSEKHYXDPIXMPXUQSGFYMUYBD9CMEZEVIAJAOQY9BNWW9SSHQSPF" 26 | "MRDPGOTTHGJYJCWDGNGMOERGLWACCSRSVS9BPMDYDVLKRTEMVTYBVTZXYPJJWITXGCBAIVBPNP" 27 | "YPPMDFXISEVOOGXPDFPHHRDF9QPHMHKKCHOFSCHWSBVNURI9OMBDCWNGFLNSUZYLTCLQYRHDBG" 28 | "XK9QKQVNGDSEEDKRHKKJYMJHHKKCHTDZTLPZ99H9AFGFPHMNFX9IHCTCCEWAJRQKLNKDQASOZV" 29 | "DKLELBHKKLERVASSJJKFBOWSS9QHOKITYRNLVVTBFBTQMLACOQHZLIDXROFCGZQEZCDIIXVLYW" 30 | "AKANHWOAVXDYDVIUVAVPDQDGPTJEDZBHSVLPDACRYMBH9YTKXYDJJCPYYJ9YSRMTUCAGICHUWW" 31 | "ZNPFVAD9XYYJEFOBAHFUWZBWVJFJCOBJMPENYHYFPFCUNDNSOBROOLPKFKXJTGIXIEESNDNRJE" 32 | "HABFCMTCHPUUM9UVDKGGC9DVNGOFKDLZRL9FFTEIYZQXKHKSLD9H9NRKBGMABDNBQHYSMHQ9WP" 33 | "DTGV9WWPSNDEUWSFQGXSLITWWTYLYUXWFDBKETVEFVRVUGUVKKMPNQCE9BPMSIVQNKAEBRDABE" 34 | "CJPCGRWRISMUAAPYTEGIKALVXZFMGJCLECRAJBCEYKFLUSPGUIQXLRYABUHBEGWPRMBPLXQGON" 35 | "LMXIDZUB9NPDKGOHQNTVEUTYIWLBVLSYTSQFRDZODQOMAFSMXPIGKPYPQYHUBSQUUES9VFVSXO" 36 | "O9ATNXWOXOGCTADMQIORRFSKPAVHQP9KXULNFWIOBGNVYNQT9DHMBMLTYCJZOFDRRWNWZLHSTX" 37 | "9VCIPAPDXHXFXSWO9KSEDWCTNLFYSVYARMWGAUNHVGNJEMKUPOSXBPQQGYEPSBVYYOEWXPUXZS" 38 | "IJAOTYFPUMGSRMXIWFOHNSHCRWLGXXXRZOQSKYSMV9IRMYCKIARXDHLRVBDVIQZUGDBEVGMXXG" 39 | "RAOU9MMHYZYIWWHKAISKQMDWGVZ9DPQEUQEFRRNKYWIVRSWNFDICIGIWIANMEVIUDYPVVXOWXQ" 40 | "MFPXGB9ZUUQXZXWFFLBVPXPKFCCZIGFXWWCHOQKG9ISFND9BYPLWWKSXZXGXEOKUNMKKLVNLBZ" 41 | "ZLGSOZKLUXHMU9RVA9DWDVZORALEFEJBPHSTKFYKYOUUHGJCMTQP9QGDKYPYRGVBTBBTBANS9M" 42 | "LDCYBWQTKOJBRPYGLATNLPMTHMF9LMRWDRBDXIWALRDXENEBVVHURXDVJBKOXEAKIZNATPZZBQ" 43 | "WDTXLYTBCRRZZYYLWBRFG9TTOBGLRCQRGJDH9XINHSQMRGPCWRGDPZLWTEEB9RFHGVXEQHDWAH" 44 | "KLEOD9EBTSYBM9AKMI9DAJAKITFZYFTDMID99MDMFXBHLHOGMSAZFTELQEBSJPCLGPUHSZCDXK" 45 | "URSLVLAXNAUWRPGUBKMUTYEEHGZICNZEXZBDVINBMSM9EIDKVQXEECPITPQLSNUPAPAZ9DDVCN" 46 | "9RDUMQUNXE9IBBBDTX9EM9ALRXQEWMONECCTSPQKJ9FFIBXXN9WKFIDBRLOVJSHZEXBOJAGXLV" 47 | "HMFZWAVGWHCLQNSUSAOIMMBNQOA9YLZCZKWYPIPNZ", 48 | "TKNSJNOADOTWPGZXEVSFQSLF9DAVKMIS9FPRPBWMQXYWOLSDZFHGILPWTPAECIUEVHAHQUCHNI" 49 | "ZOECUUWCUQUCJKESYHQCVPXBXAEJVRZMNZJUMQISLBNDQXVPZKEQGNHSPKVWGQQFKGKJSYCKHV" 50 | "AYQSMDJDTTSSTADSAZTRZBQQMFJWIQNGRYMJLHV9QESLKEPZQXEHRNZXHYFEEMYDZXGYORZYIK" 51 | "SVGMYLP9BLGYGIEZKIK9WTXFPEOSQFASMH9LFXXQPGGAAXIANPPMAKBXWAUQNCDPDEIPHASEFN" 52 | "LQKYSQCBEKPODCXESKJ9TLJXUJNZMBEUUYYKBKZPNU9KOVKFJSYNEESFBUKIODZLUIOLIN9ZKB" 53 | "CGVRZQGCUHGRZAQUANSPX9PUHGHHDLPSJU9AGRBDZAMIRTOGRZNKLBIEMBISDRXBNB9ZDGA9KV" 54 | "IHGFHVGZHJRLNTUWCAIHISNN9ORBMLHZKUEVC9UKQWOXAIA9XLJGESLUKJEKMXCTSLUAERLM9Z" 55 | "YPOGWULWQKOQOEJNAYGIXCTLRDJIBUOTJNUZQCPQVGASKKYSBMHMTUCXSEUHF9BSDWNPOZTDYV" 56 | "NNGWIBJUDZQSCMXPGKHNOGAEXVWINWNUHHFVQNXYUR9EQIRLWEJGNZAWOZHJUTOZRPVEGJSQXK" 57 | "IXMJFDCXRIBBVTYECW9FTPBGTDLPYC9GPMNIWOJBKQTKULMMXCQYBDQKKYYHLRWMRRKADULVHL" 58 | "ZMWPHLSHX9UWLDYWNHZZEOPYLDJHGTFCTZZKFNSCTTRSLLUVEWHCEUTYOMFPKLAYESZB9WUZYY" 59 | "LCLAUBBTXLGPOSQE99DLHEWNZLBYVNKBNVLZUXFYODYYQYBIIUSWBNKZDQCCWECDKUKQWQQLIE" 60 | "NDDNMPZQSKYMXUKPXZMUTVDAZXCGADITLCFXD9ZHOCDCQREXZYADLIICFTIDDAECSEUUNRWXSK" 61 | "ETYPPXZKIDGWLXD9LRKJIDLFHPIWNTGCX9CLVLHSIPBZJBBFLOVSNVOHUAXQWCWFJPVB9AQEES" 62 | "XAKZVILAVQFIQQXSWTZGMACSJLXXZ9ZIYQMRGFPFTIEOKTDOOEZFOLLSEBU9VBVNDKH9EZYHXJ" 63 | "UCT9KQZYMNUCFYOQIAOBZJ9WPULBBMEIZFRDXWOCFEZDCHMMOOMIOCAAPXSBVZKNAQ9PQHSULW" 64 | "OBNMDKENFZJZBFV9XBRDXFORFWDMXE9CZSHTYIYNRH9ZFORIDH9HXAPHLVCMRACACNNHKFKWKL" 65 | "EBVVWTDBKWBWLNHKSYWRVWADEVTCLELQOXWHSDEUZKHYSXUYKGRMBEIZOEJZEDGFLOBGUPKPWY" 66 | "CS9IZQAJABEGPIXWXWUSESOIXTHECBJQMZPTNGWUEGZDDCGONTZUERVSIJIOLQDKKMXPNEGIYE" 67 | "9CSTU9OPJRFEKLHAANY9XPMSZWWLXNVDKCMGLQYKGAUDJLCVFQBWZTFUBDBCJYIISYLMDHOSFV" 68 | "RGJALDHYPITFLSMQYXHXWXEPX9ATBUIYRZSBNCUPOQAOYDIDWVUDPFLMLRWNVSHG9YMUSWVZAU" 69 | "FWDMQQDSAUWCZAXFIJRTOM99IVI9LNWUSFQFRNMENE9JNSGG9ZLEZKZQPCURGBHMRCASDUFBZY" 70 | "LKHWNOSUBDNDVGNZQLP9RYQKYKDQUCPOIACSLAVFLWXKQPRAPHCQHTRKTFQZODQXIKLDROA9AV" 71 | "VSMYTXZDFBJRT9TBYHVABUKKRXQPJOBXIG9RCRYWBJLHANXHYOMRNJRFC9PBKDJEPDDGJNETCH" 72 | "ZNTCVDOSXUBTAKFXQQOEYNNAUNAWODERT9VMHLUTAEESTGQHOQGDSFDWLNYHWWOSXSNMPNCZGP" 73 | "WYZMUYGFZLSFCJILU9BRAKPLCUKL9S9KG99FQYIROWIDMZBTKLWNHTRKZPBCKBZORNUWAHLYPU" 74 | "UWMYHAKRNUNUFGCECOXAXSPGATZJLOXMTTXSCWEVPTUFRVKVIGOAPXJPFUFUEJGZEAAIQOQAGF" 75 | "LG9YMUZAYQOKOZKK9NLRZWUNAIACOCBYYWJVTEGUJKKUBZDZAHFGNKUCHHWLIHMHRRMHBSBJNZ" 76 | "ZCVSOPJLPPHTPGXXOVQOGQIUBPEKIEAYWZHKNQXZDMBEBFRYNMFGAOFTFBQQARMRLSROUGDKMA" 77 | "LWQSDZGLVMOZPGNHEV9FMVEDJRZD9OOXMUQRWSNJB", 78 | "CPKQROLQEATHPLWMRDPIIJHWBDQDUUTFVWQSSDABSEWLIYAWSOUWTMO9ARNLISROQHW9FEMH9Y" 79 | "E9ZQCOXVNZSHTLHURLZMTVDXJDOKBQBGXOOGSQMKMAZWHBMSZSZOJVQUXRFNXGGOAUIN9YMEDA" 80 | "QQNDXLDRYYUEJZHVRP9FYPZQWDEDGWK9KYKKBFSRDROLFZBBWUB9DBIRIOSGHMYPEJQYNDOXVF" 81 | "OQZYTWCFTDMBN9VN9G9TATYOJXQHOVVONMLIYWGADINDDZMPA9J9FNPMIBLFNVEBWGHOPIBUOZ" 82 | "EEFGHVYKDAVAT9MHACOZKUFTGGRWUKJZDKCWZOLQ9BANRAQAQWRLHDYELUAVJOFZHFCZSVPOEE" 83 | "HGUITMRZLSBZCCZ9XDMER9TPXJHBZWYO9L9MQFLHAGJ9SRUUANZOQZMRKWAXRNVHNWAFEETVVP" 84 | "KQOSTSBDKLDSQFWNKLBJECFIRYFV9YVJYKDDVXJRZAZPWSTPBL9RA9CGESRTUYDOHTAEBCOHSJ" 85 | "PQRDAIARHEBFWZNU9FUGPWIGCGSUUUHQSLAAUHZUQFHQYTRNZDYSBXMAC9JQEAKSHRFCCGVMUL" 86 | "WUOPHQSQOKZADUWBGYVPWBE9JNTYRMBPMWNFTTBADDBFTUKTAAMEGKDWXHNBHKDUURGTIQNBVX" 87 | "YHQLLAP9QLXUAHHLOXBKKFCZVRFQSVM9OOEXMFSMBHQISRBGEIXVXJCOUSCUPICRXYBMQBPJSC" 88 | "LIWHDX9JQAHDDBZBAPQVDEWKARBDYKKRRZXPGFYJSUDZYERWDUVJFSPOJKJY9QJEFAZWRBODXT" 89 | "9HYJKLDQFPFPXGZLLNFURQXRKKAVGWUDUFEJOBWZUNPNXNBEWCUFDVNTPHIOOHW9HUYDKYRQKV" 90 | "SKD9YGZLDBQT9ULRQMRQZXXSIHGODYXWGIUMEOIBYUCKKLKAPFUHXRTJBQJAPWTQRYURQICZCQ" 91 | "FLKMM9W9YXGHTDWTAQADGGPGDSJILXQAOXOFWCWOGKOCD99WHUGXAMFBNBFUVOATVPXMN9DISL" 92 | "ONXRRTB9SQGBIUURWIXLAYHBI9ORVLNVATGAYTFOLNLYGENLAJETTPWVJWNDERAAFXHWUMUDZJ" 93 | "N9FPYZMHUVOPWOO99AE9UNIBZICBOYOTTMOZWWJPRBWAE9HTBPAE9HDVGKPXITTJYDRDXVBZKF" 94 | "E9EKFGOWNY9WHMDTSYIETYNZUUBTEGAGDLVSUJWNBKHOFOGQMVABVZJXWZ9SQLBRFMUMBWBRVU" 95 | "PVFJRCZUVQLNEUYMHBZODLDTOVGXCAZCIBHHHYX9MTX9CNTGGXGCYWWMHACHHIFEG9JRCSTGNF" 96 | "TBBZBFXTOF9B9LENFPFBEZQZZILGKQYTONOBXONDBIM9DZEYTVVHZFOSNKVMQILXQRGUEITRCN" 97 | "F9QJXVMSR9LUJFENFQPGKKAIVZDFXBGAZUCGCOXLUOHAAGFJKSGWROPYUYRSNAJRIDYWRWTVCL" 98 | "VMRBIFUZCQCKEEPKZPXDYHSGNHEUCNAPTMQPYNQTLW9MPOEQEZYOUZOLPFDRHULRJBNGWIIQRU" 99 | "DSQYF9AJEQZJOKBPLQWKN9DNXYL9IDOHXWFEQXLRQDIJUSMMUSOQLEXSPUMWLBUYMZXUZYCKLT" 100 | "9SEVXPSZWERLUPQWGDEGXUAPJNLMKUAAWZZKOHUNQMIHESGI9CMPXSQIDJBIQNCDSUQLUYFKYC" 101 | "WWUHEFDXEVIERJMVBTQRIGKU9JDOQNW9RUCHHQVUU9LSUEGFXMUIEMG9UFUQJIUT9IUNLHQKUT" 102 | "BBEDQWELSBUMNDUSVBRJZK9LENSAPM9FRHRIJNNIKYIVYQJUSLKHGJQKOKIIOWAUOZRGZTQXTV" 103 | "LTTGCY9MAKCLZGFGLVBSCB99EJ99PRGJRDSDO9AT9NNM9DNPNQXBVVOTTREJKEOYZKSDDEHZEJ" 104 | "FLKTKLKDRXAKWQFDM9OBISSPCMU9JYHWWEFXICKGHPSMBFLOCKUKIRWHSBAJXVUPLCMEZCHXQ9" 105 | "DPHJN99WJOOOMRWM9CFMXKVAXR9RFPANVWGRAUKZRZPGEEXYMCJFWTUHFTUPEIDFHXLAFG9NMN" 106 | "CWQDPIVXOXB9WHGEVOTYXANITICWGTFTEWLVXFSIQQUSQEKUJTUOLBIWOIFNQOIHUVTCEAUVMM" 107 | "YVYTLBFXSBIISGCAWSQJPEUZBMTEBXMUBVNCEFFMB"}; 108 | 109 | static void generate_signature(const unsigned char *seed_bytes, 110 | uint32_t address_idx, uint8_t security, 111 | char *signature) 112 | { 113 | SIGNING_CTX ctx; 114 | unsigned char signature_fragment[SIGNATURE_FRAGMENT_SIZE * NUM_HASH_BYTES]; 115 | 116 | signing_initialize(&ctx, 0, seed_bytes, address_idx, security, 117 | NORMALIZED_HASH); 118 | 119 | for (int i = 0; i < NUM_SIGNATURE_FRAGMENTS(security); i++) { 120 | 121 | const int fragment_index = 122 | signing_next_fragment(&ctx, signature_fragment); 123 | assert_int_equal(fragment_index, i); 124 | 125 | bytes_to_chars(signature_fragment, 126 | signature + i * SIGNATURE_FRAGMENT_SIZE * 81, 127 | SIGNATURE_FRAGMENT_SIZE * NUM_HASH_BYTES); 128 | } 129 | } 130 | 131 | static void assert_signature_equals(uint8_t security, const char *signature) 132 | { 133 | for (unsigned int i = 0; i < security; i++) { 134 | // create zero terminated fragment 135 | const size_t fragment_length = strlen(FRAGMENTS[i]); 136 | char fragment[fragment_length + 1]; 137 | memcpy(fragment, signature + i * 27 * NUM_HASH_TRYTES, fragment_length); 138 | fragment[fragment_length] = '\0'; 139 | 140 | assert_string_equal(fragment, FRAGMENTS[i]); 141 | } 142 | } 143 | 144 | static void test_signature_level_one(void **state) 145 | { 146 | UNUSED(state); 147 | 148 | const uint8_t security = 1; 149 | 150 | unsigned char seed_bytes[NUM_HASH_BYTES]; 151 | chars_to_bytes(PETER_SEED, seed_bytes, NUM_HASH_TRYTES); 152 | 153 | char signature[MAX_SIGNATURE_LENGTH + 1] = {0}; 154 | generate_signature(seed_bytes, IDX, security, signature); 155 | 156 | assert_signature_equals(security, signature); 157 | } 158 | 159 | static void test_signature_level_two(void **state) 160 | { 161 | UNUSED(state); 162 | 163 | const uint8_t security = 2; 164 | 165 | unsigned char seed_bytes[NUM_HASH_BYTES]; 166 | chars_to_bytes(PETER_SEED, seed_bytes, NUM_HASH_TRYTES); 167 | 168 | char signature[MAX_SIGNATURE_LENGTH + 1] = {0}; 169 | generate_signature(seed_bytes, IDX, security, signature); 170 | 171 | assert_signature_equals(security, signature); 172 | } 173 | 174 | static void test_signature_level_three(void **state) 175 | { 176 | UNUSED(state); 177 | 178 | const uint8_t security = 3; 179 | 180 | unsigned char seed_bytes[NUM_HASH_BYTES]; 181 | chars_to_bytes(PETER_SEED, seed_bytes, NUM_HASH_TRYTES); 182 | 183 | char signature[MAX_SIGNATURE_LENGTH + 1] = {0}; 184 | generate_signature(seed_bytes, IDX, security, signature); 185 | 186 | assert_signature_equals(security, signature); 187 | } 188 | 189 | int main(void) 190 | { 191 | const struct CMUnitTest tests[] = { 192 | cmocka_unit_test(test_signature_level_one), 193 | cmocka_unit_test(test_signature_level_two), 194 | cmocka_unit_test(test_signature_level_three)}; 195 | 196 | return cmocka_run_group_tests(tests, NULL, NULL); 197 | } 198 | -------------------------------------------------------------------------------- /demos/python/test_main_tx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #******************************************************************************* 3 | #* Ledger Blue 4 | #* (c) 2016 Ledger 5 | #* 6 | #* Licensed under the Apache License, Version 2.0 (the "License"); 7 | #* you may not use this file except in compliance with the License. 8 | #* You may obtain a copy of the License at 9 | #* 10 | #* http://www.apache.org/licenses/LICENSE-2.0 11 | #* 12 | #* Unless required by applicable law or agreed to in writing, software 13 | #* distributed under the License is distributed on an "AS IS" BASIS, 14 | #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | #* See the License for the specific language governing permissions and 16 | #* limitations under the License. 17 | #******************************************************************************** 18 | from ledgerblue.comm import getDongle 19 | from ledgerblue.commException import CommException 20 | from secp256k1 import PublicKey 21 | import time 22 | 23 | 24 | def assert_exception(dongle, apdu, exception_code): 25 | try: 26 | dongle.exchange(apdu) 27 | raise Exception("Expected exception not raised") 28 | except CommException as e: 29 | if e.sw != exception_code: 30 | raise Exception("Unexpected exception status %04x" % e.sw) 31 | 32 | 33 | dongle = getDongle(True) 34 | exceptionCount = 0 35 | start_time = time.time() 36 | 37 | test_addr = "ADR" * 27 + "\0" 38 | bad_tag = "TAG" * 9 + "9\0" 39 | 40 | 41 | # ------------------------------------------ 42 | # Wrong initial byte 43 | assert_exception(dongle, bytes("70200020FF".decode('hex') + "5\0"), 0x6E00) 44 | 45 | dongle.exchange(bytes("8020000000".decode('hex'))) 46 | 47 | # ------------------------------------------ 48 | # Not rcv last index first (this is Cur flag) 49 | assert_exception(dongle, bytes("80200010FF".decode('hex') + "0\0"), 0x6803) 50 | 51 | # ------------------------------------------ 52 | # Last idx too small 53 | assert_exception(dongle, bytes("80200020FF".decode('hex') + "1\0"), 0x6802) 54 | 55 | # ------------------------------------------ 56 | # Last idx negative 57 | assert_exception(dongle, bytes("80200020FF".decode('hex') + "-1\0"), 0x6802) 58 | 59 | # ------------------------------------------ 60 | # Last idx too large 61 | assert_exception(dongle, bytes("80200020FF".decode('hex') + "9\0"), 0x6802) 62 | 63 | # ------------------------------------------ 64 | # Cur out of order 65 | dongle.exchange(bytes("80200020FF".decode('hex') + "5\0")) # set last idx 66 | assert_exception(dongle, bytes("80200010FF".decode('hex') + "1\0"), 0x6805) 67 | 68 | # ------------------------------------------ 69 | # Cur idx negative 70 | dongle.exchange(bytes("80200020FF".decode('hex') + "5\0")) # set last idx 71 | 72 | # TODO : this does not throw anymore due to overflow detection 73 | assert_exception(dongle, bytes("80200010FF".decode('hex') + "-1\0"), 0x6802) 74 | 75 | # ------------------------------------------ 76 | # Previous TX incomplete 77 | dongle.exchange(bytes("80200020FF".decode('hex') + "5\0")) # set last idx 78 | dongle.exchange(bytes("80200010FF".decode('hex') + "0\0")) # set cur idx 79 | assert_exception(dongle, bytes("80200010FF".decode('hex') + "1\0"), 0x6802) 80 | 81 | # ------------------------------------------ 82 | # Addr too short 83 | dongle.exchange(bytes("80200020FF".decode('hex') + "5\0")) # set last idx 84 | dongle.exchange(bytes("80200010FF".decode('hex') + "0\0")) # set cur idx 85 | assert_exception(dongle, bytes( 86 | "8020000151".decode('hex') + test_addr[1:]), 0x6802) 87 | 88 | # ------------------------------------------ 89 | # Addr too long 90 | dongle.exchange(bytes("80200020FF".decode('hex') + "5\0")) # set last idx 91 | dongle.exchange(bytes("80200010FF".decode('hex') + "0\0")) # set cur idx 92 | assert_exception(dongle, bytes( 93 | "8020000151".decode('hex') + "A" + test_addr), 0x6802) 94 | 95 | # ------------------------------------------ 96 | # Addr invalid character(s) 97 | dongle.exchange(bytes("80200020FF".decode('hex') + "5\0")) # set last idx 98 | dongle.exchange(bytes("80200010FF".decode('hex') + "0\0")) # set cur idx 99 | assert_exception(dongle, bytes("8020000151".decode( 100 | 'hex') + "1" + test_addr[1:]), 0x6802) 101 | 102 | # ------------------------------------------ 103 | # First tx not output 104 | dongle.exchange(bytes("80200020FF".decode('hex') + "5\0")) # set last idx 105 | dongle.exchange(bytes("80200010FF".decode('hex') + "0\0")) # set cur idx 106 | dongle.exchange(bytes("8020000151".decode('hex') + test_addr)) # set addr 107 | assert_exception(dongle, bytes("80200002FF".decode('hex') + "-200\0"), 0x6802) 108 | 109 | # ------------------------------------------ 110 | # Tag invalid char(s) 111 | dongle.exchange(bytes("80200020FF".decode('hex') + "5\0")) # set last idx 112 | dongle.exchange(bytes("80200010FF".decode('hex') + "0\0")) # set cur idx 113 | dongle.exchange(bytes("8020000151".decode('hex') + test_addr)) # set addr 114 | dongle.exchange(bytes("80200002FF".decode('hex') + "200\0")) # set val 115 | assert_exception(dongle, bytes("802000041B".decode('hex') + "123\0"), 0x6802) 116 | 117 | # ------------------------------------------ 118 | # Previous TX incomplete - missing time 119 | dongle.exchange(bytes("80200020FF".decode('hex') + "5\0")) # set last idx 120 | dongle.exchange(bytes("80200010FF".decode('hex') + "0\0")) # set cur idx 121 | dongle.exchange(bytes("8020000151".decode('hex') + test_addr)) # set addr 122 | dongle.exchange(bytes("80200002FF".decode('hex') + "200\0")) # set val 123 | dongle.exchange(bytes("802000041B".decode('hex') + "TAG\0")) # set tag 124 | assert_exception(dongle, bytes("80200010FF".decode('hex') + "1\0"), 0x6802) 125 | 126 | # ------------------------------------------ 127 | # Input missing seed idx 128 | dongle.exchange(bytes("80200020FF".decode('hex') + "5\0")) # set last idx 129 | dongle.exchange(bytes("80200010FF".decode('hex') + "0\0")) # set cur idx 130 | dongle.exchange(bytes("8020000151".decode('hex') + test_addr)) # set addr 131 | dongle.exchange(bytes("80200002FF".decode('hex') + "-5\0")) # set val 132 | dongle.exchange(bytes("802000041B".decode('hex') + "TAG\0")) # set tag 133 | assert_exception(dongle, bytes("80200108FF".decode('hex') + "99999\0"), 0x6802) 134 | 135 | # ------------------------------------------ 136 | # Input missing input address 137 | dongle.exchange(bytes("80200020FF".decode('hex') + "5\0")) # set last idx 138 | dongle.exchange(bytes("80200010FF".decode('hex') + "0\0")) # set cur idx 139 | dongle.exchange(bytes("80200040FF".decode('hex') + "4\0")) # set input idx 140 | dongle.exchange(bytes("80200002FF".decode('hex') + "-5\0")) # set val 141 | dongle.exchange(bytes("802000041B".decode('hex') + "TAG\0")) # set tag 142 | assert_exception(dongle, bytes("80200108FF".decode('hex') + "99999\0"), 0x6802) 143 | 144 | # ------------------------------------------ 145 | # Input no space for meta tx 146 | dongle.exchange(bytes("80200020FF".decode('hex') + "5\0")) # set last idx 147 | dongle.exchange(bytes("80200010FF".decode('hex') + "0\0")) # set cur idx 148 | dongle.exchange(bytes("8020000151".decode('hex') + test_addr)) # set addr 149 | dongle.exchange(bytes("80200040FF".decode('hex') + "4\0")) # set input idx 150 | dongle.exchange(bytes("80200002FF".decode('hex') + "-5\0")) # set val 151 | dongle.exchange(bytes("802000041B".decode('hex') + "TAG\0")) # set tag 152 | dongle.exchange(bytes("80200008FF".decode('hex') + "99999\0")) # set time 153 | assert_exception(dongle, bytes("80200010FF".decode('hex') + "1\0"), 0x6802) 154 | 155 | # ------------------------------------------ 156 | # Duplicate cur idx 157 | dongle.exchange(bytes("80200020FF".decode('hex') + "5\0")) # set last idx 158 | dongle.exchange(bytes("80200010FF".decode('hex') + "0\0")) # set cur idx 159 | dongle.exchange(bytes("8020000151".decode('hex') + test_addr)) # set addr 160 | dongle.exchange(bytes("80200002FF".decode('hex') + "10\0")) # set val 161 | dongle.exchange(bytes("802000041B".decode('hex') + "TAG\0")) # set tag 162 | dongle.exchange(bytes("80200008FF".decode('hex') + "99999\0")) # set time 163 | assert_exception(dongle, bytes("80200010FF".decode('hex') + "0\0"), 0x6802) 164 | 165 | # ------------------------------------------ 166 | # Input index on output tx 167 | dongle.exchange(bytes("80200020FF".decode('hex') + "5\0")) # set last idx 168 | dongle.exchange(bytes("80200010FF".decode('hex') + "0\0")) # set cur idx 169 | dongle.exchange(bytes("8020000151".decode('hex') + test_addr)) # set addr 170 | dongle.exchange(bytes("80200040FF".decode('hex') + "4\0")) # set input idx 171 | dongle.exchange(bytes("80200002FF".decode('hex') + "10\0")) # set val 172 | dongle.exchange(bytes("802000041B".decode('hex') + "TAG\0")) # set tag 173 | dongle.exchange(bytes("80200008FF".decode('hex') + "99999\0")) # set time 174 | assert_exception(dongle, bytes("80200010FF".decode('hex') + "0\0"), 0x6802) 175 | 176 | # ------------------------------------------ 177 | # Too many TX's 178 | dongle.exchange(bytes("80200020FF".decode('hex') + "2\0")) # set last idx 179 | 180 | dongle.exchange(bytes("80200010FF".decode('hex') + "0\0")) # set cur idx 181 | dongle.exchange(bytes("8020000151".decode('hex') + test_addr)) # set addr 182 | dongle.exchange(bytes("80200040FF".decode('hex') + "4\0")) # set input idx 183 | dongle.exchange(bytes("80200002FF".decode('hex') + "-5\0")) # set val 184 | dongle.exchange(bytes("802000041B".decode('hex') + "TAG\0")) # set tag 185 | dongle.exchange(bytes("80200008FF".decode('hex') + "99999\0")) # set time 186 | 187 | dongle.exchange(bytes("80200010FF".decode('hex') + "2\0")) # set cur idx 188 | dongle.exchange(bytes("8020000151".decode('hex') + test_addr)) # set addr 189 | dongle.exchange(bytes("80200002FF".decode('hex') + "10\0")) # set val 190 | dongle.exchange(bytes("802000041B".decode('hex') + "TAG\0")) # set tag 191 | dongle.exchange(bytes("80200008FF".decode('hex') + "99999\0")) # set time 192 | 193 | assert_exception(dongle, bytes("80200010FF".decode('hex') + "3\0"), 0x6802) 194 | 195 | # ------------------------------------------ 196 | # Complete transmission flag sent too early 197 | dongle.exchange(bytes("80200020FF".decode('hex') + "2\0")) # set last idx 198 | 199 | dongle.exchange(bytes("80200010FF".decode('hex') + "0\0")) # set cur idx 200 | dongle.exchange(bytes("8020000151".decode('hex') + test_addr)) # set addr 201 | dongle.exchange(bytes("80200040FF".decode('hex') + "4\0")) # set input idx 202 | dongle.exchange(bytes("80200002FF".decode('hex') + "-5\0")) # set val 203 | dongle.exchange(bytes("802000041B".decode('hex') + "TAG\0")) # set tag 204 | 205 | assert_exception(dongle, bytes("80200108FF".decode('hex') + "99999\0"), 0x6802) 206 | 207 | # ------------------------------------------ 208 | # other tests/considerations: 209 | 210 | # ------------------------------------------ 211 | # cur/last idx, val, time = int 212 | # addr, tag = chars 213 | 214 | # any non null-terminated int terminates as soon as a non-int 215 | # is found. non null-terminated char is same as invalid char 216 | # and/or addr/tag too long 217 | 218 | # ------------------------------------------ 219 | # Tag too long - only reads in at most 27 220 | 221 | # os_memcpy(tx_tag, msg, MIN(27, len)); 222 | 223 | # ------------------------------------------ 224 | # Integer under/overflow 225 | 226 | # ------------------------------------------ 227 | # Negative timestamp 228 | 229 | # ------------------------------------------ 230 | # Transmission not "complete" but bundle is 231 | 232 | # ------------------------------------------ 233 | # Length byte or null terminator ends the msg 234 | 235 | # ------------------------------------------ 236 | # Length byte 0 237 | 238 | 239 | elapsed_time = time.time() - start_time 240 | print "Time Elapsed: %d - Exceptions: %d" % (elapsed_time, exceptionCount) 241 | -------------------------------------------------------------------------------- /src/keccak/sha3.c: -------------------------------------------------------------------------------- 1 | /* sha3.c - an implementation of Secure Hash Algorithm 3 (Keccak). 2 | * based on the 3 | * The Keccak SHA-3 submission. Submission to NIST (Round 3), 2011 4 | * by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche 5 | * 6 | * Copyright: 2013 Aleksey Kravchenko 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a 9 | * copy of this software and associated documentation files (the "Software"), 10 | * to deal in the Software without restriction, including without limitation 11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 | * and/or sell copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so. 14 | * 15 | * This program is distributed in the hope that it will be useful, but 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 17 | * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | #include "sha3.h" 24 | #include "macros.h" 25 | 26 | #define I64(x) x##LL 27 | #define ROTL64(qword, n) ((qword) << (n) ^ ((qword) >> (64 - (n)))) 28 | #define le2me_64(x) (x) 29 | #define IS_ALIGNED_64(p) (0 == (7 & ((const char *)(p) - (const char *)0))) 30 | #define me64_to_le_str(to, from, length) memcpy((to), (from), (length)) 31 | 32 | /* constants */ 33 | #define NumberOfRounds 24 34 | 35 | /* SHA3 (Keccak) constants for 24 rounds */ 36 | static uint64_t keccak_round_constants[NumberOfRounds] = { 37 | I64(0x0000000000000001), I64(0x0000000000008082), I64(0x800000000000808A), 38 | I64(0x8000000080008000), I64(0x000000000000808B), I64(0x0000000080000001), 39 | I64(0x8000000080008081), I64(0x8000000000008009), I64(0x000000000000008A), 40 | I64(0x0000000000000088), I64(0x0000000080008009), I64(0x000000008000000A), 41 | I64(0x000000008000808B), I64(0x800000000000008B), I64(0x8000000000008089), 42 | I64(0x8000000000008003), I64(0x8000000000008002), I64(0x8000000000000080), 43 | I64(0x000000000000800A), I64(0x800000008000000A), I64(0x8000000080008081), 44 | I64(0x8000000000008080), I64(0x0000000080000001), I64(0x8000000080008008)}; 45 | 46 | /* Initializing a sha3 context for given number of output bits */ 47 | static void keccak_Init(SHA3_CTX *ctx, unsigned bits) 48 | { 49 | /* NB: The Keccak capacity parameter = bits * 2 */ 50 | unsigned rate = 1600 - bits * 2; 51 | 52 | memset(ctx, 0, sizeof(SHA3_CTX)); 53 | ctx->block_size = rate / 8; 54 | assert(rate <= 1600 && (rate % 64) == 0); 55 | } 56 | 57 | /** 58 | * Initialize context before calculating hash. 59 | * 60 | * @param ctx context to initialize 61 | */ 62 | void sha3_224_Init(SHA3_CTX *ctx) 63 | { 64 | keccak_Init(ctx, 224); 65 | } 66 | 67 | /** 68 | * Initialize context before calculating hash. 69 | * 70 | * @param ctx context to initialize 71 | */ 72 | void sha3_256_Init(SHA3_CTX *ctx) 73 | { 74 | keccak_Init(ctx, 256); 75 | } 76 | 77 | /** 78 | * Initialize context before calculating hash. 79 | * 80 | * @param ctx context to initialize 81 | */ 82 | void sha3_384_Init(SHA3_CTX *ctx) 83 | { 84 | keccak_Init(ctx, 384); 85 | } 86 | 87 | /** 88 | * Initialize context before calculating hash. 89 | * 90 | * @param ctx context to initialize 91 | */ 92 | void sha3_512_Init(SHA3_CTX *ctx) 93 | { 94 | keccak_Init(ctx, 512); 95 | } 96 | 97 | /* Keccak theta() transformation */ 98 | static void keccak_theta(uint64_t *A) 99 | { 100 | unsigned int x; 101 | uint64_t C[5], D[5]; 102 | 103 | for (x = 0; x < 5; x++) { 104 | C[x] = A[x] ^ A[x + 5] ^ A[x + 10] ^ A[x + 15] ^ A[x + 20]; 105 | } 106 | D[0] = ROTL64(C[1], 1) ^ C[4]; 107 | D[1] = ROTL64(C[2], 1) ^ C[0]; 108 | D[2] = ROTL64(C[3], 1) ^ C[1]; 109 | D[3] = ROTL64(C[4], 1) ^ C[2]; 110 | D[4] = ROTL64(C[0], 1) ^ C[3]; 111 | 112 | for (x = 0; x < 5; x++) { 113 | A[x] ^= D[x]; 114 | A[x + 5] ^= D[x]; 115 | A[x + 10] ^= D[x]; 116 | A[x + 15] ^= D[x]; 117 | A[x + 20] ^= D[x]; 118 | } 119 | } 120 | 121 | /* Keccak pi() transformation */ 122 | static void keccak_pi(uint64_t *A) 123 | { 124 | uint64_t A1; 125 | A1 = A[1]; 126 | A[1] = A[6]; 127 | A[6] = A[9]; 128 | A[9] = A[22]; 129 | A[22] = A[14]; 130 | A[14] = A[20]; 131 | A[20] = A[2]; 132 | A[2] = A[12]; 133 | A[12] = A[13]; 134 | A[13] = A[19]; 135 | A[19] = A[23]; 136 | A[23] = A[15]; 137 | A[15] = A[4]; 138 | A[4] = A[24]; 139 | A[24] = A[21]; 140 | A[21] = A[8]; 141 | A[8] = A[16]; 142 | A[16] = A[5]; 143 | A[5] = A[3]; 144 | A[3] = A[18]; 145 | A[18] = A[17]; 146 | A[17] = A[11]; 147 | A[11] = A[7]; 148 | A[7] = A[10]; 149 | A[10] = A1; 150 | /* note: A[ 0] is left as is */ 151 | } 152 | 153 | /* Keccak chi() transformation */ 154 | static void keccak_chi(uint64_t *A) 155 | { 156 | int i; 157 | for (i = 0; i < 25; i += 5) { 158 | uint64_t A0 = A[0 + i], A1 = A[1 + i]; 159 | A[0 + i] ^= ~A1 & A[2 + i]; 160 | A[1 + i] ^= ~A[2 + i] & A[3 + i]; 161 | A[2 + i] ^= ~A[3 + i] & A[4 + i]; 162 | A[3 + i] ^= ~A[4 + i] & A0; 163 | A[4 + i] ^= ~A0 & A1; 164 | } 165 | } 166 | 167 | static void sha3_permutation(uint64_t *state) 168 | { 169 | int round; 170 | for (round = 0; round < NumberOfRounds; round++) { 171 | keccak_theta(state); 172 | 173 | /* apply Keccak rho() transformation */ 174 | state[1] = ROTL64(state[1], 1); 175 | state[2] = ROTL64(state[2], 62); 176 | state[3] = ROTL64(state[3], 28); 177 | state[4] = ROTL64(state[4], 27); 178 | state[5] = ROTL64(state[5], 36); 179 | state[6] = ROTL64(state[6], 44); 180 | state[7] = ROTL64(state[7], 6); 181 | state[8] = ROTL64(state[8], 55); 182 | state[9] = ROTL64(state[9], 20); 183 | state[10] = ROTL64(state[10], 3); 184 | state[11] = ROTL64(state[11], 10); 185 | state[12] = ROTL64(state[12], 43); 186 | state[13] = ROTL64(state[13], 25); 187 | state[14] = ROTL64(state[14], 39); 188 | state[15] = ROTL64(state[15], 41); 189 | state[16] = ROTL64(state[16], 45); 190 | state[17] = ROTL64(state[17], 15); 191 | state[18] = ROTL64(state[18], 21); 192 | state[19] = ROTL64(state[19], 8); 193 | state[20] = ROTL64(state[20], 18); 194 | state[21] = ROTL64(state[21], 2); 195 | state[22] = ROTL64(state[22], 61); 196 | state[23] = ROTL64(state[23], 56); 197 | state[24] = ROTL64(state[24], 14); 198 | 199 | keccak_pi(state); 200 | keccak_chi(state); 201 | 202 | /* apply iota(state, round) */ 203 | *state ^= keccak_round_constants[round]; 204 | } 205 | } 206 | 207 | /** 208 | * The core transformation. Process the specified block of data. 209 | * 210 | * @param hash the algorithm state 211 | * @param block the message block to process 212 | * @param block_size the size of the processed block in bytes 213 | */ 214 | static void sha3_process_block(uint64_t hash[25], const uint64_t *block, 215 | size_t block_size) 216 | { 217 | /* expanded loop */ 218 | hash[0] ^= le2me_64(block[0]); 219 | hash[1] ^= le2me_64(block[1]); 220 | hash[2] ^= le2me_64(block[2]); 221 | hash[3] ^= le2me_64(block[3]); 222 | hash[4] ^= le2me_64(block[4]); 223 | hash[5] ^= le2me_64(block[5]); 224 | hash[6] ^= le2me_64(block[6]); 225 | hash[7] ^= le2me_64(block[7]); 226 | hash[8] ^= le2me_64(block[8]); 227 | /* if not sha3-512 */ 228 | if (block_size > 72) { 229 | hash[9] ^= le2me_64(block[9]); 230 | hash[10] ^= le2me_64(block[10]); 231 | hash[11] ^= le2me_64(block[11]); 232 | hash[12] ^= le2me_64(block[12]); 233 | /* if not sha3-384 */ 234 | if (block_size > 104) { 235 | hash[13] ^= le2me_64(block[13]); 236 | hash[14] ^= le2me_64(block[14]); 237 | hash[15] ^= le2me_64(block[15]); 238 | hash[16] ^= le2me_64(block[16]); 239 | /* if not sha3-256 */ 240 | if (block_size > 136) { 241 | hash[17] ^= le2me_64(block[17]); 242 | #ifdef FULL_SHA3_FAMILY_SUPPORT 243 | /* if not sha3-224 */ 244 | if (block_size > 144) { 245 | hash[18] ^= le2me_64(block[18]); 246 | hash[19] ^= le2me_64(block[19]); 247 | hash[20] ^= le2me_64(block[20]); 248 | hash[21] ^= le2me_64(block[21]); 249 | hash[22] ^= le2me_64(block[22]); 250 | hash[23] ^= le2me_64(block[23]); 251 | hash[24] ^= le2me_64(block[24]); 252 | } 253 | #endif 254 | } 255 | } 256 | } 257 | /* make a permutation of the hash */ 258 | sha3_permutation(hash); 259 | } 260 | 261 | #define SHA3_FINALIZED 0x80000000 262 | 263 | /** 264 | * Calculate message hash. 265 | * Can be called repeatedly with chunks of the message to be hashed. 266 | * 267 | * @param ctx the algorithm context containing current hashing state 268 | * @param msg message chunk 269 | * @param size length of the message chunk 270 | */ 271 | void sha3_Update(SHA3_CTX *ctx, const unsigned char *msg, size_t size) 272 | { 273 | size_t idx = (size_t)ctx->rest; 274 | size_t block_size = (size_t)ctx->block_size; 275 | 276 | if (ctx->rest & SHA3_FINALIZED) 277 | return; /* too late for additional input */ 278 | ctx->rest = (unsigned)((ctx->rest + size) % block_size); 279 | 280 | /* fill partial block */ 281 | if (idx) { 282 | size_t left = block_size - idx; 283 | memcpy((char *)ctx->message + idx, msg, (size < left ? size : left)); 284 | if (size < left) 285 | return; 286 | 287 | /* process partial block */ 288 | sha3_process_block(ctx->hash, ctx->message, block_size); 289 | msg += left; 290 | size -= left; 291 | } 292 | while (size >= block_size) { 293 | uint64_t *aligned_message_block; 294 | if (IS_ALIGNED_64(msg)) { 295 | /* the most common case is processing of an already aligned message 296 | without copying it */ 297 | aligned_message_block = (uint64_t *)(void *)msg; 298 | } 299 | else { 300 | memcpy(ctx->message, msg, block_size); 301 | aligned_message_block = ctx->message; 302 | } 303 | 304 | sha3_process_block(ctx->hash, aligned_message_block, block_size); 305 | msg += block_size; 306 | size -= block_size; 307 | } 308 | if (size) { 309 | memcpy(ctx->message, msg, size); /* save leftovers */ 310 | } 311 | } 312 | 313 | /** 314 | * Store calculated hash into the given array. 315 | * 316 | * @param ctx the algorithm context containing current hashing state 317 | * @param result calculated hash in binary form 318 | */ 319 | void sha3_Final(SHA3_CTX *ctx, unsigned char *result) 320 | { 321 | size_t digest_length = 100 - ctx->block_size / 2; 322 | const size_t block_size = ctx->block_size; 323 | 324 | if (!(ctx->rest & SHA3_FINALIZED)) { 325 | /* clear the rest of the data queue */ 326 | memset((char *)ctx->message + ctx->rest, 0, block_size - ctx->rest); 327 | ((char *)ctx->message)[ctx->rest] |= 0x06; 328 | ((char *)ctx->message)[block_size - 1] |= 0x80; 329 | 330 | /* process final block */ 331 | sha3_process_block(ctx->hash, ctx->message, block_size); 332 | ctx->rest = SHA3_FINALIZED; /* mark context as finalized */ 333 | } 334 | 335 | assert(block_size > digest_length); 336 | if (result) 337 | me64_to_le_str(result, ctx->hash, digest_length); 338 | MEMSET_BZERO(ctx, sizeof(SHA3_CTX)); 339 | } 340 | 341 | #if USE_KECCAK 342 | /** 343 | * Store calculated hash into the given array. 344 | * 345 | * @param ctx the algorithm context containing current hashing state 346 | * @param result calculated hash in binary form 347 | */ 348 | void keccak_Final(SHA3_CTX *ctx, unsigned char *result) 349 | { 350 | size_t digest_length = 100 - ctx->block_size / 2; 351 | const size_t block_size = ctx->block_size; 352 | 353 | if (!(ctx->rest & SHA3_FINALIZED)) { 354 | /* clear the rest of the data queue */ 355 | memset((char *)ctx->message + ctx->rest, 0, block_size - ctx->rest); 356 | ((char *)ctx->message)[ctx->rest] |= 0x01; 357 | ((char *)ctx->message)[block_size - 1] |= 0x80; 358 | 359 | /* process final block */ 360 | sha3_process_block(ctx->hash, ctx->message, block_size); 361 | ctx->rest = SHA3_FINALIZED; /* mark context as finalized */ 362 | } 363 | 364 | assert(block_size > digest_length); 365 | if (result) 366 | me64_to_le_str(result, ctx->hash, digest_length); 367 | MEMSET_BZERO(ctx, sizeof(SHA3_CTX)); 368 | } 369 | 370 | void keccak_256(const unsigned char *data, size_t len, unsigned char *digest) 371 | { 372 | SHA3_CTX ctx; 373 | keccak_256_Init(&ctx); 374 | keccak_Update(&ctx, data, len); 375 | keccak_Final(&ctx, digest); 376 | } 377 | 378 | void keccak_512(const unsigned char *data, size_t len, unsigned char *digest) 379 | { 380 | SHA3_CTX ctx; 381 | keccak_512_Init(&ctx); 382 | keccak_Update(&ctx, data, len); 383 | keccak_Final(&ctx, digest); 384 | } 385 | #endif /* USE_KECCAK */ 386 | 387 | void sha3_256(const unsigned char *data, size_t len, unsigned char *digest) 388 | { 389 | SHA3_CTX ctx; 390 | sha3_256_Init(&ctx); 391 | sha3_Update(&ctx, data, len); 392 | sha3_Final(&ctx, digest); 393 | } 394 | 395 | void sha3_512(const unsigned char *data, size_t len, unsigned char *digest) 396 | { 397 | SHA3_CTX ctx; 398 | sha3_512_Init(&ctx); 399 | sha3_Update(&ctx, data, len); 400 | sha3_Final(&ctx, digest); 401 | } 402 | -------------------------------------------------------------------------------- /tests/tx_test.c: -------------------------------------------------------------------------------- 1 | #include "test_common.h" 2 | #include 3 | #include "transaction_file.h" 4 | #include "api_tests.h" 5 | #include "api.h" 6 | #include "aux.h" 7 | #include "iota/bundle.h" 8 | #include "iota/conversion.h" 9 | 10 | void derive_seed_bip32(const unsigned int *path, unsigned int pathLength, 11 | unsigned char *seed_bytes) 12 | { 13 | UNUSED(path); 14 | UNUSED(pathLength); 15 | 16 | chars_to_bytes(mock_ptr_type(char *), seed_bytes, NUM_HASH_TRYTES); 17 | } 18 | 19 | void io_send(const void *ptr, unsigned int length, unsigned short sw) 20 | { 21 | check_expected(ptr); 22 | check_expected(length); 23 | check_expected(sw); 24 | } 25 | 26 | static void test_valid_bundle(const char *seed, int security, 27 | const TX_INPUT *tx, int last_index, 28 | const char *bundle_hash) 29 | { 30 | SEED_INIT(seed); 31 | api_initialize(); 32 | { 33 | SET_SEED_INPUT input = {BIP32_PATH, security}; 34 | EXPECT_API_OK(set_seed, input); 35 | } 36 | for (int i = 0; i < last_index; i++) { 37 | TX_OUTPUT output = {0}; 38 | output.finalized = false; 39 | 40 | EXPECT_API_DATA_OK(tx, tx[i], output); 41 | } 42 | { 43 | TX_OUTPUT output = {0}; 44 | output.finalized = true; 45 | strncpy(output.bundle_hash, bundle_hash, 81); 46 | 47 | EXPECT_API_DATA_OK(tx, tx[last_index], output); 48 | } 49 | } 50 | 51 | static void test_bundles_for_seed_from_file(void **state) 52 | { 53 | UNUSED(state); 54 | 55 | void test(char *seed, TX_INPUT *txs, char *bundle_hash, 56 | char signatures[][SIGNATURE_LENGTH]) 57 | { 58 | UNUSED(signatures); 59 | 60 | test_valid_bundle(seed, 2, txs, 5, bundle_hash); 61 | } 62 | 63 | test_for_each_bundle("generateBundlesForSeed", test); 64 | } 65 | 66 | static void finalize_bundle(TX_INPUT *tx, int last_index) 67 | { 68 | BUNDLE_CTX bundle; 69 | bundle_initialize(&bundle, last_index); 70 | 71 | for (int i = 0; i <= last_index; i++) { 72 | tx[i].current_index = i; 73 | tx[i].last_index = last_index; 74 | rpad_chars(tx[i].tag, tx[i].tag, 27); 75 | 76 | bundle_set_internal_address(&bundle, tx[i].address, tx[i].address_idx); 77 | bundle_add_tx(&bundle, tx[i].value, tx[i].tag, tx[i].timestamp); 78 | } 79 | 80 | uint32_t tag_increment = bundle_finalize(&bundle); 81 | 82 | char extended_tag[NUM_HASH_TRYTES]; 83 | unsigned char tag_bytes[NUM_HASH_BYTES]; 84 | rpad_chars(extended_tag, tx[0].tag, NUM_HASH_TRYTES); 85 | chars_to_bytes(extended_tag, tag_bytes, NUM_HASH_TRYTES); 86 | 87 | bytes_add_u32_mem(tag_bytes, tag_increment); 88 | bytes_to_chars(tag_bytes, extended_tag, NUM_HASH_BYTES); 89 | 90 | memcpy(tx[0].tag, extended_tag, 27); 91 | } 92 | 93 | /** Test that the bundle finalization above is correct. */ 94 | static void test_refinalize_valid_bundle(void **state) 95 | { 96 | UNUSED(state); 97 | static const int security = 2; 98 | 99 | TX_INPUT txs[8]; 100 | // output transaction 101 | memcpy(&txs[0], &PETER_VECTOR.bundle[0], sizeof(TX_INPUT)); 102 | txs[0].tag[0] = '\0'; 103 | // input transaction 104 | memcpy(&txs[1], &PETER_VECTOR.bundle[1], sizeof(TX_INPUT)); 105 | // meta transaction 106 | memcpy(&txs[2], &PETER_VECTOR.bundle[2], sizeof(TX_INPUT)); 107 | 108 | // create a valid bundle 109 | finalize_bundle(txs, 2); 110 | 111 | test_valid_bundle(PETER_VECTOR.seed, security, txs, 2, 112 | PETER_VECTOR.bundle_hash); 113 | } 114 | 115 | static void test_invalid_input_address_index(void **state) 116 | { 117 | UNUSED(state); 118 | static const int security = 2; 119 | 120 | TX_INPUT txs[8]; 121 | // output transaction 122 | memcpy(&txs[0], &PETER_VECTOR.bundle[0], sizeof(TX_INPUT)); 123 | // input transaction 124 | memcpy(&txs[1], &PETER_VECTOR.bundle[1], sizeof(TX_INPUT)); 125 | txs[1].address_idx += 1; 126 | // meta transaction 127 | memcpy(&txs[2], &PETER_VECTOR.bundle[2], sizeof(TX_INPUT)); 128 | txs[2].address_idx += 1; 129 | 130 | // create a valid bundle 131 | finalize_bundle(txs, 2); 132 | 133 | SEED_INIT(PETER_VECTOR.seed); 134 | api_initialize(); 135 | { 136 | SET_SEED_INPUT input = {BIP32_PATH, security}; 137 | EXPECT_API_OK(set_seed, input); 138 | } 139 | { // output transaction 140 | TX_OUTPUT output = {0}; 141 | output.finalized = false; 142 | 143 | EXPECT_API_DATA_OK(tx, txs[0], output); 144 | } 145 | { // input transaction 146 | TX_OUTPUT output = {0}; 147 | output.finalized = false; 148 | 149 | EXPECT_API_DATA_OK(tx, txs[1], output); 150 | } 151 | { // meta transaction 152 | EXPECT_API_EXCEPTION(tx, txs[2]); 153 | } 154 | } 155 | 156 | static void test_invalid_tx_order(void **state) 157 | { 158 | UNUSED(state); 159 | static const int security = 2; 160 | 161 | SEED_INIT(PETER_VECTOR.seed); 162 | api_initialize(); 163 | { 164 | SET_SEED_INPUT input = {BIP32_PATH, security}; 165 | EXPECT_API_OK(set_seed, input); 166 | } 167 | { // input transaction as the first transaction 168 | TX_INPUT input; 169 | memcpy(&input, &PETER_VECTOR.bundle[1], sizeof(input)); 170 | input.current_index = 0; 171 | 172 | EXPECT_API_EXCEPTION(tx, input); 173 | } 174 | } 175 | 176 | static void test_tx_index_twice(void **state) 177 | { 178 | UNUSED(state); 179 | static const int security = 2; 180 | 181 | SEED_INIT(PETER_VECTOR.seed); 182 | api_initialize(); 183 | { 184 | SET_SEED_INPUT input = {BIP32_PATH, security}; 185 | EXPECT_API_OK(set_seed, input); 186 | } 187 | { 188 | TX_INPUT input; 189 | memcpy(&input, &PETER_VECTOR.bundle[0], sizeof(input)); 190 | TX_OUTPUT output = {0}; 191 | output.finalized = false; 192 | 193 | EXPECT_API_DATA_OK(tx, input, output); 194 | } 195 | { 196 | TX_INPUT input; 197 | memcpy(&input, &PETER_VECTOR.bundle[1], sizeof(input)); 198 | input.current_index = 0; 199 | 200 | EXPECT_API_EXCEPTION(tx, input); 201 | } 202 | } 203 | 204 | static void test_missing_meta_tx(void **state) 205 | { 206 | UNUSED(state); 207 | static const int security = 2; 208 | 209 | TX_INPUT txs[8]; 210 | // output transaction 211 | memcpy(&txs[0], &PETER_VECTOR.bundle[0], sizeof(TX_INPUT)); 212 | // input transaction 213 | memcpy(&txs[1], &PETER_VECTOR.bundle[1], sizeof(TX_INPUT)); 214 | 215 | // create a valid bundle 216 | finalize_bundle(txs, 1); 217 | 218 | SEED_INIT(PETER_VECTOR.seed); 219 | api_initialize(); 220 | { 221 | SET_SEED_INPUT input = {BIP32_PATH, security}; 222 | EXPECT_API_OK(set_seed, input); 223 | } 224 | { // output transaction 225 | TX_OUTPUT output = {0}; 226 | output.finalized = false; 227 | 228 | EXPECT_API_DATA_OK(tx, txs[0], output); 229 | } 230 | { // input transaction 231 | EXPECT_API_EXCEPTION(tx, txs[1]); 232 | } 233 | } 234 | 235 | static void test_missing_meta_tx_with_change(void **state) 236 | { 237 | UNUSED(state); 238 | static const int security = 2; 239 | 240 | TX_INPUT txs[8]; 241 | // output transaction 242 | memcpy(&txs[0], &PETER_VECTOR.bundle[0], sizeof(TX_INPUT)); 243 | // input transaction 244 | memcpy(&txs[1], &PETER_VECTOR.bundle[1], sizeof(TX_INPUT)); 245 | // 0-value change transaction 246 | memcpy(&txs[2], &PETER_VECTOR.bundle[0], sizeof(TX_INPUT)); 247 | txs[2].value = 0; 248 | 249 | // create a valid bundle 250 | finalize_bundle(txs, 2); 251 | 252 | SEED_INIT(PETER_VECTOR.seed); 253 | api_initialize(); 254 | { 255 | SET_SEED_INPUT input = {BIP32_PATH, security}; 256 | EXPECT_API_OK(set_seed, input); 257 | } 258 | { // output transaction 259 | TX_OUTPUT output = {0}; 260 | output.finalized = false; 261 | 262 | EXPECT_API_DATA_OK(tx, txs[0], output); 263 | } 264 | { // input transaction 265 | TX_OUTPUT output = {0}; 266 | output.finalized = false; 267 | 268 | EXPECT_API_DATA_OK(tx, txs[1], output); 269 | } 270 | { // 0-value change transaction 271 | EXPECT_API_EXCEPTION(tx, txs[2]); 272 | } 273 | } 274 | 275 | static void test_meta_tx_without_reference(void **state) 276 | { 277 | UNUSED(state); 278 | static const int security = 2; 279 | 280 | SEED_INIT(PETER_VECTOR.seed); 281 | api_initialize(); 282 | { 283 | SET_SEED_INPUT input = {BIP32_PATH, security}; 284 | EXPECT_API_OK(set_seed, input); 285 | } 286 | 287 | int tx_index = 0; 288 | const int last_index = 2; 289 | { // output transaction 290 | TX_INPUT input; 291 | memcpy(&input, &PETER_VECTOR.bundle[0], sizeof(input)); 292 | input.current_index = tx_index++; 293 | input.last_index = last_index; 294 | 295 | TX_OUTPUT output = {0}; 296 | output.finalized = false; 297 | 298 | EXPECT_API_DATA_OK(tx, input, output); 299 | } 300 | { // meta transaction 301 | TX_INPUT input; 302 | memcpy(&input, &PETER_VECTOR.bundle[2], sizeof(input)); 303 | input.current_index = tx_index++; 304 | input.last_index = last_index; 305 | 306 | EXPECT_API_EXCEPTION(tx, input); 307 | } 308 | } 309 | 310 | static void test_invalid_change_index(void **state) 311 | { 312 | UNUSED(state); 313 | static const int security = 2; 314 | 315 | TX_INPUT txs[8]; 316 | // output transaction 317 | memcpy(&txs[0], &PETER_VECTOR.bundle[0], sizeof(TX_INPUT)); 318 | txs[0].value -= 1; 319 | // input transaction 320 | memcpy(&txs[1], &PETER_VECTOR.bundle[1], sizeof(TX_INPUT)); 321 | // meta transaction 322 | memcpy(&txs[2], &PETER_VECTOR.bundle[2], sizeof(TX_INPUT)); 323 | // valued change transaction 324 | memcpy(&txs[3], &PETER_VECTOR.bundle[1], sizeof(TX_INPUT)); 325 | memcpy(txs[3].address, PETER_VECTOR.addresses[security][3], 326 | NUM_HASH_TRYTES); 327 | txs[3].value = 1; 328 | txs[3].address_idx = 9999; 329 | 330 | // create a valid bundle 331 | finalize_bundle(txs, 3); 332 | 333 | SEED_INIT(PETER_VECTOR.seed); 334 | api_initialize(); 335 | { 336 | SET_SEED_INPUT input = {BIP32_PATH, security}; 337 | EXPECT_API_OK(set_seed, input); 338 | } 339 | { // output transaction 340 | TX_OUTPUT output = {0}; 341 | output.finalized = false; 342 | 343 | EXPECT_API_DATA_OK(tx, txs[0], output); 344 | } 345 | { // input transaction 346 | TX_OUTPUT output = {0}; 347 | output.finalized = false; 348 | 349 | EXPECT_API_DATA_OK(tx, txs[1], output); 350 | } 351 | { // meta transaction 352 | TX_OUTPUT output = {0}; 353 | output.finalized = false; 354 | 355 | EXPECT_API_DATA_OK(tx, txs[2], output); 356 | } 357 | { // 0-value change transaction 358 | EXPECT_API_EXCEPTION(tx, txs[3]); 359 | } 360 | } 361 | 362 | static void test_change_address_reuses_input(void **state) 363 | { 364 | UNUSED(state); 365 | static const int security = 2; 366 | 367 | TX_INPUT txs[8]; 368 | // output transaction 369 | memcpy(&txs[0], &PETER_VECTOR.bundle[0], sizeof(TX_INPUT)); 370 | txs[0].value -= 1; 371 | // input transaction 372 | memcpy(&txs[1], &PETER_VECTOR.bundle[1], sizeof(TX_INPUT)); 373 | // meta transaction 374 | memcpy(&txs[2], &PETER_VECTOR.bundle[2], sizeof(TX_INPUT)); 375 | // valued change transaction to input address 376 | memcpy(&txs[3], &PETER_VECTOR.bundle[1], sizeof(TX_INPUT)); 377 | txs[3].value = 1; 378 | 379 | // create a valid bundle 380 | finalize_bundle(txs, 3); 381 | 382 | SEED_INIT(PETER_VECTOR.seed); 383 | api_initialize(); 384 | { 385 | SET_SEED_INPUT input = {BIP32_PATH, security}; 386 | EXPECT_API_OK(set_seed, input); 387 | } 388 | { // output transaction 389 | TX_OUTPUT output = {0}; 390 | output.finalized = false; 391 | 392 | EXPECT_API_DATA_OK(tx, txs[0], output); 393 | } 394 | { // input transaction 395 | TX_OUTPUT output = {0}; 396 | output.finalized = false; 397 | 398 | EXPECT_API_DATA_OK(tx, txs[1], output); 399 | } 400 | { // meta transaction 401 | TX_OUTPUT output = {0}; 402 | output.finalized = false; 403 | 404 | EXPECT_API_DATA_OK(tx, txs[2], output); 405 | } 406 | { // 0-value change transaction 407 | EXPECT_API_EXCEPTION(tx, txs[3]); 408 | } 409 | } 410 | 411 | static void test_invalid_value_transaction(void **state) 412 | { 413 | UNUSED(state); 414 | static const int security = 2; 415 | 416 | SEED_INIT(PETER_VECTOR.seed); 417 | api_initialize(); 418 | { 419 | SET_SEED_INPUT input = {BIP32_PATH, security}; 420 | EXPECT_API_OK(set_seed, input); 421 | } 422 | { // output transaction 423 | TX_INPUT input; 424 | memcpy(&input, &PETER_VECTOR.bundle[0], sizeof(input)); 425 | input.value = MAX_IOTA_VALUE + 1; 426 | 427 | EXPECT_API_EXCEPTION(tx, input); 428 | } 429 | } 430 | 431 | static void test_not_set_seed(void **state) 432 | { 433 | UNUSED(state); 434 | 435 | api_initialize(); 436 | { 437 | TX_INPUT input; 438 | memcpy(&input, &PETER_VECTOR.bundle[0], sizeof(input)); 439 | 440 | EXPECT_API_EXCEPTION(tx, input); 441 | } 442 | } 443 | 444 | int main(void) 445 | { 446 | const struct CMUnitTest tests[] = { 447 | cmocka_unit_test(test_bundles_for_seed_from_file), 448 | cmocka_unit_test(test_refinalize_valid_bundle), 449 | cmocka_unit_test(test_invalid_input_address_index), 450 | cmocka_unit_test(test_invalid_tx_order), 451 | cmocka_unit_test(test_tx_index_twice), 452 | cmocka_unit_test(test_missing_meta_tx), 453 | cmocka_unit_test(test_missing_meta_tx_with_change), 454 | cmocka_unit_test(test_meta_tx_without_reference), 455 | cmocka_unit_test(test_invalid_change_index), 456 | cmocka_unit_test(test_change_address_reuses_input), 457 | cmocka_unit_test(test_invalid_value_transaction), 458 | cmocka_unit_test(test_not_set_seed)}; 459 | 460 | return cmocka_run_group_tests(tests, NULL, NULL); 461 | } 462 | -------------------------------------------------------------------------------- /src/iota/conversion.c: -------------------------------------------------------------------------------- 1 | #include "conversion.h" 2 | #include 3 | #include "common.h" 4 | 5 | // #define USE_UNSAFE_INCREMENT_TAG 6 | 7 | #define INT_LENGTH 12 8 | // base of the ternary system 9 | #define BASE 3 10 | 11 | // the middle of the domain described by 242 trits, i.e. \sum_{k=0}^{241} 3^k 12 | static const uint32_t HALF_3[12] = { 13 | 0xa5ce8964, 0x9f007669, 0x1484504f, 0x3ade00d9, 0x0c24486e, 0x50979d57, 14 | 0x79a4c702, 0x48bbae36, 0xa9f6808b, 0xaa06a805, 0xa87fabdf, 0x5e69ebef}; 15 | 16 | // the two's complement of HALF_3_u, i.e. ~HALF_3_u + 1 17 | static const uint32_t NEG_HALF_3[12] = { 18 | 0x5a31769c, 0x60ff8996, 0xeb7bafb0, 0xc521ff26, 0xf3dbb791, 0xaf6862a8, 19 | 0x865b38fd, 0xb74451c9, 0x56097f74, 0x55f957fa, 0x57805420, 0xa1961410}; 20 | 21 | // representing the value of the highes trit in the feasible domain, i.e 3^242 22 | static const uint32_t TRIT_243[12] = { 23 | 0x4b9d12c9, 0x3e00ecd3, 0x2908a09f, 0x75bc01b2, 0x184890dc, 0xa12f3aae, 24 | 0xf3498e04, 0x91775c6c, 0x53ed0116, 0x540d500b, 0x50ff57bf, 0xbcd3d7df}; 25 | 26 | #ifdef USE_UNSAFE_INCREMENT_TAG 27 | // representing the value of the 82nd trit, i.e. 3^81 28 | static const uint32_t TRIT_82[12] = {0xd56d7cc3, 0xb6bf0c69, 0xa149e834, 29 | 0x4d98d5ce, 0x1}; 30 | #endif // USE_UNSAFE_INCREMENT_TAG 31 | 32 | static const trit_t trits_mapping[27][3] = { 33 | {-1, -1, -1}, {0, -1, -1}, {1, -1, -1}, {-1, 0, -1}, {0, 0, -1}, {1, 0, -1}, 34 | {-1, 1, -1}, {0, 1, -1}, {1, 1, -1}, {-1, -1, 0}, {0, -1, 0}, {1, -1, 0}, 35 | {-1, 0, 0}, {0, 0, 0}, {1, 0, 0}, {-1, 1, 0}, {0, 1, 0}, {1, 1, 0}, 36 | {-1, -1, 1}, {0, -1, 1}, {1, -1, 1}, {-1, 0, 1}, {0, 0, 1}, {1, 0, 1}, 37 | {-1, 1, 1}, {0, 1, 1}, {1, 1, 1}}; 38 | 39 | // available tryte chars in the correct order 40 | static const char tryte_to_char_mapping[] = "NOPQRSTUVWXYZ9ABCDEFGHIJKLM"; 41 | 42 | /* --------------------- trits > trytes and back */ 43 | // used for bytes to chars and back 44 | int trytes_to_trits(const tryte_t trytes_in[], trit_t trits_out[], 45 | unsigned int tryte_len) 46 | { 47 | for (unsigned int i = 0; i < tryte_len; i++) { 48 | int8_t idx = (int8_t)trytes_in[i] + 13; 49 | trits_out[i * 3 + 0] = trits_mapping[idx][0]; 50 | trits_out[i * 3 + 1] = trits_mapping[idx][1]; 51 | trits_out[i * 3 + 2] = trits_mapping[idx][2]; 52 | } 53 | return 0; 54 | } 55 | 56 | int trits_to_trytes(const trit_t trits_in[], tryte_t trytes_out[], 57 | unsigned int trit_len) 58 | { 59 | if (trit_len % 3 != 0) { 60 | return -1; 61 | } 62 | unsigned int tryte_len = trit_len / 3; 63 | 64 | for (unsigned int i = 0; i < tryte_len; i++) { 65 | trytes_out[i] = trits_in[i * 3 + 0] + trits_in[i * 3 + 1] * 3 + 66 | trits_in[i * 3 + 2] * 9; 67 | } 68 | return 0; 69 | } 70 | /* --------------------- END trits > trytes */ 71 | 72 | /* --------------------- trytes > chars and back */ 73 | // used for bytes to chars and back 74 | int chars_to_trytes(const char chars_in[], tryte_t trytes_out[], 75 | unsigned int len) 76 | { 77 | for (unsigned int i = 0; i < len; i++) { 78 | if (chars_in[i] == '9') { 79 | trytes_out[i] = 0; 80 | } 81 | else if ((int8_t)chars_in[i] >= 'N') { 82 | trytes_out[i] = (int8_t)(chars_in[i]) - 64 - 27; 83 | } 84 | else { 85 | trytes_out[i] = (int8_t)(chars_in[i]) - 64; 86 | } 87 | } 88 | return 0; 89 | } 90 | 91 | int trytes_to_chars(const tryte_t trytes_in[], char chars_out[], 92 | unsigned int len) 93 | { 94 | for (unsigned int i = 0; i < len; i++) { 95 | chars_out[i] = tryte_to_char_mapping[trytes_in[i] + 13]; 96 | } 97 | 98 | return 0; 99 | } 100 | /* --------------------- END trytes > chars */ 101 | 102 | void chars_to_trits(const char *chars, trit_t *trits, unsigned int chars_len) 103 | { 104 | tryte_t trytes[chars_len]; 105 | chars_to_trytes(chars, trytes, chars_len); 106 | trytes_to_trits(trytes, trits, chars_len); 107 | } 108 | 109 | void trits_to_chars(const trit_t *trits, char *chars, unsigned int trit_len) 110 | { 111 | tryte_t trytes[trit_len / 3]; 112 | trits_to_trytes(trits, trytes, trit_len); 113 | trytes_to_chars(trytes, chars, trit_len / 3); 114 | } 115 | 116 | /** @brief Returns true, if the long little-endian integer represents a negative 117 | * number in two's complement. 118 | */ 119 | static inline bool bigint_is_negative(const uint32_t *bigint) 120 | { 121 | // whether the most significant bit of the most significant byte is set 122 | return (bigint[12 - 1] >> (sizeof(bigint[0]) * 8 - 1) != 0); 123 | } 124 | 125 | static int bigint_cmp(const uint32_t *a, const uint32_t *b) 126 | { 127 | for (unsigned int i = 12; i-- > 0;) { 128 | if (a[i] < b[i]) { 129 | return -1; 130 | } 131 | if (a[i] > b[i]) { 132 | return 1; 133 | } 134 | } 135 | return 0; 136 | } 137 | 138 | static inline bool addcarry_u32(uint32_t *r, uint32_t a, uint32_t b, bool c_in) 139 | { 140 | const uint32_t sum = a + b + (c_in ? 1 : 0); 141 | const bool carry = (sum < a) || (c_in && (sum <= a)); 142 | 143 | *r = sum; 144 | return carry; 145 | } 146 | 147 | static bool bigint_add(uint32_t *r, const uint32_t *a, const uint32_t *b) 148 | { 149 | bool carry = false; 150 | for (unsigned int i = 0; i < 12; i++) { 151 | carry = addcarry_u32(&r[i], a[i], b[i], carry); 152 | } 153 | 154 | return carry; 155 | } 156 | 157 | static bool bigint_sub(uint32_t *r, const uint32_t *a, const uint32_t *b) 158 | { 159 | bool carry = true; 160 | for (unsigned int i = 0; i < 12; i++) { 161 | carry = addcarry_u32(&r[i], a[i], ~b[i], carry); 162 | } 163 | 164 | return carry; 165 | } 166 | 167 | /** @brief adds a single 32-bit integer to a long little-endian integer. 168 | * @return index of the most significant word which changed during the addition 169 | */ 170 | unsigned int bigint_add_u32_mem(uint32_t *a, uint32_t summand) 171 | { 172 | bool carry = addcarry_u32(&a[0], a[0], summand, false); 173 | if (carry == false) { 174 | return 0; 175 | } 176 | 177 | for (unsigned int i = 1; i < 12; i++) { 178 | carry = addcarry_u32(&a[i], a[i], 0, true); 179 | if (carry == false) { 180 | return i; 181 | } 182 | } 183 | 184 | // overflow 185 | return 12; 186 | } 187 | 188 | /** @brief multiplies a single 8-bit integer with a long little-endian integer. 189 | * @param ms_index the index of the most significant non-zero word of the 190 | * input integer. Words after this are not considered. 191 | * @return the carry (one word) of the multiplication up to the byte which has 192 | the index specified in msb_index. 193 | */ 194 | static uint32_t bigint_mult_byte_mem(uint32_t *a, uint8_t factor, 195 | unsigned int ms_index) 196 | { 197 | uint32_t carry = 0; 198 | 199 | for (unsigned int i = 0; i <= ms_index; i++) { 200 | const uint64_t v = (uint64_t)factor * a[i] + carry; 201 | 202 | carry = v >> 32; 203 | a[i] = v & 0xFFFFFFFF; 204 | } 205 | 206 | return carry; 207 | } 208 | 209 | /** @brief devides a long big-endian integer by a single 8-bit integer. 210 | * @return remainder of the integer division. 211 | */ 212 | static uint32_t bigint_div_byte_mem(uint32_t *a, uint8_t divisor) 213 | { 214 | uint32_t remainder = 0; 215 | 216 | for (unsigned int i = 12; i-- > 0;) { 217 | const uint64_t v = UINT64_C(0x100000000) * remainder + a[i]; 218 | 219 | remainder = v % divisor; 220 | a[i] = (v / divisor) & 0xFFFFFFFF; 221 | } 222 | 223 | return remainder; 224 | } 225 | 226 | /** @brief Changes number to the corresponding representation of the number 227 | * with the 242th trit set to 0. 228 | * @return true, if the number was changed, false otherwise. 229 | */ 230 | static bool bigint_set_last_trit_zero(uint32_t *bigint) 231 | { 232 | if (bigint_is_negative(bigint)) { 233 | if (bigint_cmp(bigint, NEG_HALF_3) < 0) { 234 | bigint_add(bigint, bigint, TRIT_243); 235 | return true; 236 | } 237 | } 238 | else { 239 | if (bigint_cmp(bigint, HALF_3) > 0) { 240 | bigint_sub(bigint, bigint, TRIT_243); 241 | return true; 242 | } 243 | } 244 | return false; 245 | } 246 | 247 | /* --------------------- trits > bigint and back */ 248 | static void trits_to_bigint(const trit_t *trits, uint32_t *bigint) 249 | { 250 | unsigned int ms_index = 0; // initialy there is no most significant word >0 251 | os_memset(bigint, 0, 12 * sizeof(bigint[0])); 252 | 253 | // ignore the 243th trit, as it cannot be fully represented in 48 bytes 254 | for (unsigned int i = 242; i-- > 0;) { 255 | // convert to non-balanced ternary 256 | const uint8_t trit = trits[i] + 1; 257 | 258 | const uint32_t carry = bigint_mult_byte_mem(bigint, BASE, ms_index); 259 | if (carry > 0) { 260 | // if there is carry we need to use the next higher byte 261 | bigint[++ms_index] = carry; 262 | } 263 | 264 | if (trit == 0) { 265 | // nothing to add 266 | continue; 267 | } 268 | 269 | const unsigned int last_changed_index = 270 | bigint_add_u32_mem(bigint, trit); 271 | if (last_changed_index > ms_index) { 272 | ms_index = last_changed_index; 273 | } 274 | } 275 | 276 | // convert to balanced ternary using two's complement 277 | if (bigint_cmp(bigint, HALF_3) >= 0) { 278 | bigint_sub(bigint, bigint, HALF_3); 279 | } 280 | else { 281 | // equivalent to bytes := ~(HALF_3 - bytes) + 1 282 | bigint_add(bigint, NEG_HALF_3, bigint); 283 | } 284 | } 285 | 286 | static void bigint_to_trits_mem(uint32_t *bigint, trit_t *trits) 287 | { 288 | // the two's complement represention is only correct, if the number fits 289 | // into 48 bytes, i.e. has the 243th trit set to 0 290 | bigint_set_last_trit_zero(bigint); 291 | 292 | // convert to the (positive) number representing non-balanced ternary 293 | if (bigint_is_negative(bigint)) { 294 | bigint_sub(bigint, bigint, NEG_HALF_3); 295 | } 296 | else { 297 | bigint_add(bigint, bigint, HALF_3); 298 | } 299 | 300 | // ignore the 243th trit, as it cannot be fully represented in 48 bytes 301 | for (unsigned int i = 0; i < 242; i++) { 302 | const uint32_t rem = bigint_div_byte_mem(bigint, BASE); 303 | trits[i] = rem - 1; // convert back to balanced 304 | } 305 | // set the last trit to zero for consistency 306 | trits[242] = 0; 307 | } 308 | 309 | bool int64_to_trits(int64_t value, trit_t *trits, unsigned int num_trits) 310 | { 311 | const bool is_negative = value < 0; 312 | if (is_negative) { 313 | value = -value; 314 | } 315 | 316 | os_memset(trits, 0, num_trits); 317 | 318 | for (unsigned int i = 0; i < num_trits; i++) { 319 | if (value == 0) { 320 | return false; 321 | } 322 | 323 | int rem = value % BASE; 324 | value = value / BASE; 325 | if (rem > 1) { 326 | rem = -1; 327 | value += 1; 328 | } 329 | 330 | trits[i] = is_negative ? -rem : rem; 331 | } 332 | 333 | return value != 0; 334 | } 335 | /* --------------------- END trits > bigint */ 336 | 337 | /** @brief Converts bigint consisting of 12 words into an array of bytes. 338 | * It is represented using 48bytes in big-endiean, by reversing the order of 339 | * the words. The endianness of the host machine is taken into account. 340 | */ 341 | static void bigint_to_bytes(const uint32_t *bigint, unsigned char *bytes) 342 | { 343 | // reverse word order 344 | for (unsigned int i = 12; i-- > 0; bytes += 4) { 345 | const uint32_t num = bigint[i]; 346 | 347 | bytes[0] = (num >> 24) & 0xFF; 348 | bytes[1] = (num >> 16) & 0xFF; 349 | bytes[2] = (num >> 8) & 0xFF; 350 | bytes[3] = (num >> 0) & 0xFF; 351 | } 352 | } 353 | 354 | /** @brief Converts an array of 48 bytes into a bigint consisting of 12 words. 355 | * The bigint is represented using 48bytes in big-endiean. The endianness of 356 | * the host machine is taken into account. 357 | */ 358 | static void bytes_to_bigint(const unsigned char *bytes, uint32_t *bigint) 359 | { 360 | // reverse word order 361 | for (unsigned int i = 12; i-- > 0; bytes += 4) { 362 | bigint[i] = (uint32_t)bytes[0] << 24 | (uint32_t)bytes[1] << 16 | 363 | (uint32_t)bytes[2] << 8 | (uint32_t)bytes[3] << 0; 364 | } 365 | } 366 | 367 | void trits_to_bytes(const trit_t *trits, unsigned char *bytes) 368 | { 369 | uint32_t bigint[12]; 370 | trits_to_bigint(trits, bigint); 371 | bigint_to_bytes(bigint, bytes); 372 | } 373 | 374 | void trytes_to_bytes(const tryte_t *trytes, unsigned char *bytes) 375 | { 376 | trit_t trits[243]; 377 | trytes_to_trits(trytes, trits, 81); 378 | trits_to_bytes(trits, bytes); 379 | } 380 | 381 | void chars_to_bytes(const char *chars, unsigned char *bytes, 382 | unsigned int chars_len) 383 | { 384 | for (unsigned int i = 0; i < chars_len / 81; i++) { 385 | trit_t trits[243]; 386 | chars_to_trits(chars + i * 81, trits, 81); 387 | // bigint can only handle 242 trits 388 | trits[242] = 0; 389 | 390 | trits_to_bytes(trits, bytes + i * 48); 391 | } 392 | } 393 | 394 | static inline void bytes_to_trits(const unsigned char *bytes, trit_t *trits) 395 | { 396 | uint32_t bigint[12]; 397 | bytes_to_bigint(bytes, bigint); 398 | bigint_to_trits_mem(bigint, trits); 399 | } 400 | 401 | void bytes_to_trytes(const unsigned char *bytes, tryte_t *trytes) 402 | { 403 | trit_t trits[243]; 404 | bytes_to_trits(bytes, trits); 405 | trits_to_trytes(trits, trytes, 243); 406 | } 407 | 408 | void bytes_to_chars(const unsigned char *bytes, char *chars, 409 | unsigned int bytes_len) 410 | { 411 | for (unsigned int i = 0; i < bytes_len / 48; i++) { 412 | tryte_t trytes[81]; 413 | bytes_to_trytes(bytes + i * 48, trytes); 414 | trytes_to_chars(trytes, chars + i * 81, 81); 415 | } 416 | } 417 | 418 | void bytes_set_last_trit_zero(unsigned char *bytes) 419 | { 420 | uint32_t bigint[12]; 421 | bytes_to_bigint(bytes, bigint); 422 | if (bigint_set_last_trit_zero(bigint)) { 423 | bigint_to_bytes(bigint, bytes); 424 | } 425 | } 426 | 427 | static void increment_trit_aera(trit_t *trits, unsigned int start_trit, 428 | unsigned int num_trits) 429 | { 430 | trit_t *trit = trits + start_trit; 431 | 432 | for (unsigned int i = 0; i < num_trits; i++, trit++) { 433 | if (*trit < MAX_TRIT_VALUE) { 434 | *trit += 1; 435 | break; 436 | } 437 | *trit = MIN_TRIT_VALUE; 438 | } 439 | } 440 | 441 | // TODO: there are faster and more efficient algos for this, but is it worth it? 442 | void bytes_increment_trit_area_81(unsigned char *bytes) 443 | { 444 | #ifdef USE_UNSAFE_INCREMENT_TAG 445 | uint32_t bigint[12]; 446 | bytes_to_bigint(bytes, bigint); 447 | bigint_add(bigint, bigint, TRIT_82); 448 | bigint_to_bytes(bigint, bytes); 449 | #else 450 | trit_t trits[243]; 451 | bytes_to_trits(bytes, trits); 452 | increment_trit_aera(trits, 81, 81); 453 | trits_to_bytes(trits, bytes); 454 | #endif // USE_UNSAFE_INCREMENT_TAG 455 | } 456 | 457 | void bytes_add_u32_mem(unsigned char *bytes, uint32_t summand) 458 | { 459 | if (summand > 0) { 460 | uint32_t bigint[12]; 461 | 462 | bytes_to_bigint(bytes, bigint); 463 | bigint_add_u32_mem(bigint, summand); 464 | bigint_set_last_trit_zero(bigint); 465 | bigint_to_bytes(bigint, bytes); 466 | } 467 | } 468 | --------------------------------------------------------------------------------