diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 53ab524..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Build and Test - -on: - push: - branches: ["**"] - pull_request: - branches: ["**"] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Install build tools - run: sudo apt-get update && sudo apt-get install -y libcurl4-openssl-dev - - - name: Run make - run: make - - - name: Confirm success - run: echo "Makefile executed successfully" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2798f40 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,46 @@ +name: MicroSui lib GitHub Actions CI + +on: + push: + branches: ["**"] + pull_request: + branches: ["**"] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install build tools + run: sudo apt-get update && sudo apt-get install -y libcurl4-openssl-dev + + - name: Run make + run: make + + - name: Confirm success + run: echo "Makefile executed successfully" + + selftest: + runs-on: ubuntu-latest + defaults: + run: + working-directory: examples/core_examples/benchmark + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install build tools + run: sudo apt-get update && sudo apt-get install -y libcurl4-openssl-dev + + - name: Run make + run: make + + - name: Run benchmark + run: ./benchmark.out + + - name: Confirm success + run: echo "Selftest completed successfully" diff --git a/CHANGELOG.md b/CHANGELOG.md index 36d11b4..a2fae98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## \[0.3.4] - 2026-04-07 + +This release introduces lower-level signing primitives designed for resource-constrained microcontrollers, allowing developers to work directly with pre-computed **_BLAKE2b digests_** (significantly reducing the data that needs to be transferred between components) or skip redundant steps when intermediate values such as the derived keypair are already available in memory. + +- Added `microsui_sign_ed25519_from_digest()`: accepts a pre-computed _32-byte BLAKE2b digest_ instead of the _full transaction message_, significantly reducing the data that needs to be transferred between components, a critical advantage in embedded communication pipelines where bandwidth and memory are limited. +- Added `microsui_sign_ed25519_from_keypair()`: accepts a precomputed keypair directly, avoiding redundant key derivation. +- Added new keypair derivation function. +- Improved benchmark to cover all new operations. + +--- + ## \[0.3.3] - 2026-04-01 - Added new mod functions to Monocypher library (these functions are more efficient than the compact25519 ones). diff --git a/examples/core_examples/benchmarks/Makefile b/examples/core_examples/benchmark/Makefile similarity index 96% rename from examples/core_examples/benchmarks/Makefile rename to examples/core_examples/benchmark/Makefile index 72a186d..8e01c04 100644 --- a/examples/core_examples/benchmarks/Makefile +++ b/examples/core_examples/benchmark/Makefile @@ -17,7 +17,7 @@ WIFI_UNSUPPORTED_FILE = $(MICROSUI_CORE)/impl/wifi/wifi_unsupported.c # This fil CFLAGS := -Wall -Wextra -I$(INCLUDE) -I$(SRC) -I$(MICROSUI_CORE) -I$(UTILS) -I$(LIB) -I$(MONOCYPHER) -I$(JSMN) -SOURCES := benchmarks.c \ +SOURCES := benchmark.c \ $(wildcard $(SRC)/*.c) \ $(wildcard $(MICROSUI_CORE)/*.c) \ $(wildcard $(UTILS)/*.c) \ @@ -28,7 +28,7 @@ SOURCES := benchmarks.c \ $(WIFI_DESKTOP_FILE) \ $(WIFI_UNSUPPORTED_FILE) -OUTPUT := benchmarks.out +OUTPUT := benchmark.out all: clean_before_build $(OUTPUT) diff --git a/examples/core_examples/benchmark/benchmark.c b/examples/core_examples/benchmark/benchmark.c new file mode 100644 index 0000000..79cdcbf --- /dev/null +++ b/examples/core_examples/benchmark/benchmark.c @@ -0,0 +1,285 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MicroSui.h" + +// Sui Message in hex format (this message must be signed) +const char* message_hex = "00000200080065cd1d0000000000202e3d52393c9035afd1ef38abd7fce2dad71f0e276b522fb274f4e14d1df974720202000101000001010300000000010100d79a4c7a655aa80cf92069bbac9666705f1d7181ff9c2d59efbc7e6ec4c3379d0180dc491e55e7caabfcdd1b0f538928d8d54107b9c1def3ed0baa3aa5106ba8674f0dd01400000000204b7e9da00f30cd1edf4d40710213c15a862e1fc175f2edb2b2c870c8559d65cdd79a4c7a655aa80cf92069bbac9666705f1d7181ff9c2d59efbc7e6ec4c3379de80300000000000040ab3c000000000000"; + +// You must place your seed private key (in bytes format) here. +const uint8_t private_key_seed[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +uint8_t random_digest[32] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F +}; + +uint64_t millis(void); + +int main() { + printf("\n\t\t\t --- MicroSui Operations Benchmarks ---\n"); + + printf("\n\t This benchmark will measure the execution time of key operations in the MicroSui library, including signature generation and verification, as well as key management functions.\n"); + printf("\t The time taken for each operation will be displayed in milliseconds (ms) or microseconds (us) depending on the duration, providing insights into the performance of the library's core functionalities, and may vary significantly based on the underlying hardware and optimizations.\n\n"); + + printf("\n\tOriginal Message: '%s'", message_hex); + + // 0. Converting the message from hex to bytes + size_t message_len = strlen(message_hex) / 2; // 2 hex chars = 1 byte + uint8_t message[message_len]; + hex_to_bytes(message_hex, message, message_len); + + // 0. Generate public and secret key + uint8_t secret_key[64]; + uint8_t public_key[32]; + microsui_derive_keypair_ed25519(secret_key, public_key, private_key_seed); + + /////// SIGNATURES /////// + // 1. Generating the Sui Signature from the message and seed private key + printf("\n\n\n 1 - Generating Signature from Sui Message...\n"); + uint8_t sui_sig[97]; + + uint64_t start_time = millis(); + int sign_result = microsui_sign_ed25519(sui_sig, message, message_len, private_key_seed); + uint64_t end_time = millis(); + if(sign_result == 0) { + printf("\t Signature created successfully\n\n"); + } else { + printf("\t Signature creation failed --\n"); + printf("\t Error code: %d -- Aborting\n\n", sign_result); + return -1; + } + + // Calculate the time taken for verification + uint64_t time_taken_signature = end_time - start_time; + + // 2. Generating the Sui Signature from the precalculated digest and secret key + printf(" 2 - Generating Signature from precalculated Digest...\n"); + uint8_t sui_sig_from_digest[97]; + + start_time = millis(); + microsui_sign_ed25519_from_digest(sui_sig_from_digest, random_digest, secret_key, public_key); + end_time = millis(); + + uint64_t time_taken_signature_from_digest = end_time - start_time; + printf("\t Signature from digest created successfully\n\n"); + + // 3. Verifying the Sui Signature from the message and signature + printf(" 3 - Verifying Signature from Sui Message...\n"); + start_time = millis(); + int verification_result = microsui_verify_signature(sui_sig, message, message_len); + end_time = millis(); + + // Calculate the time taken for verification + uint64_t time_taken_verification = end_time - start_time; + if(verification_result == 0) { + printf("\t Signature verified successfully\n\n"); + } else { + printf("\t Signature verification failed --\n"); + printf("\t Error code: %d -- Aborting\n\n", verification_result); + return -1; + } + + // 4. Verifying the Sui Signature from the precalculated digest and signature + printf(" 4 - Verifying Signature from precalculated Digest...\n"); + start_time = millis(); + int verification_result_from_digest = microsui_verify_signature_ed25519_from_digest(sui_sig_from_digest, random_digest); + end_time = millis(); + + // Calculate the time taken for verification + uint64_t time_taken_verification_from_digest = end_time - start_time; + if(verification_result_from_digest == 0) { + printf("\t Signature from digest verified successfully\n\n"); + } else { + printf("\t Signature verification failed --\n"); + printf("\t Error code: %d -- Aborting\n\n", verification_result_from_digest); + return -1; + } + + /////// KEY MANAGEMENT /////// + printf(" 5 - Deriving a Public Key from a 32-byte Ed25519 seed private key...\n"); + uint8_t derived_public_key_out[32]; + start_time = millis(); + int public_key_derivation_result = microsui_derive_public_key_ed25519(derived_public_key_out, private_key_seed); + end_time = millis(); + uint64_t time_taken_public_key_derivation = end_time - start_time; + if(public_key_derivation_result == 0) { + printf("\t Public key derived successfully.\n\n"); + } else { + printf("\t Public key derivation failed --\n\n"); + printf("\t Error code: %d -- Aborting\n\n", public_key_derivation_result); + return -1; + } + + printf(" 6 - Deriving a Keypair (Secret Key + Public Key) from a 32-byte Ed25519 seed private key...\n"); + u_int8_t derived_secret_key_out[64]; + uint8_t derived_public_key_out2[32]; + start_time = millis(); + int keypair_derivation_result = microsui_derive_keypair_ed25519(derived_secret_key_out, derived_public_key_out2, private_key_seed); + end_time = millis(); + uint64_t time_taken_keypair_derivation = end_time - start_time; + if(keypair_derivation_result == 0) { + printf("\t Keypair derived successfully.\n\n"); + } else { + printf("\t Keypair derivation failed --\n\n"); + printf("\t Error code: %d -- Aborting\n\n", keypair_derivation_result); + return -1; + } + + printf(" 7 - Deriving a Sui address from a 32-byte Ed25519 public key...\n"); + uint8_t encoded_address[32]; + start_time = millis(); + int address_derivation_result = microsui_derive_sui_address_ed25519(encoded_address, public_key); + end_time = millis(); + uint64_t time_taken_address_derivation = end_time - start_time; + + if(address_derivation_result == 0) { + printf("\t Sui address derived successfully. Address: 0x"); + for (int i = 0; i < 32; i++) printf("%02x", encoded_address[i]); + printf("\n\n"); + } else { + printf("\t Sui address derivation failed --\n\n"); + printf("\t Error code: %d -- Aborting\n\n", address_derivation_result); + return -1; + } + + + /////// KEY MANAGEMENT - BECH32 OPERATIONS /////// + printf(" 8 - Encoding the 32-byte seed into a Sui Bech32 private key string....\n"); + char private_key_bech32_output[71]; + start_time = millis(); + int encoding_result = microsui_encode_bech32_private_key(private_key_bech32_output, random_digest); + end_time = millis(); + + // Calculate the time taken for encoding + uint64_t time_taken_encoding = end_time - start_time; + if(encoding_result == 0) { + printf("\t Encoding successful. Encoded Bech32 Key: %s\n\n", private_key_bech32_output); + } else { + printf("\t Encoding failed --\n"); + printf("\t Error code: %d -- Aborting\n\n", encoding_result); + return -1; + } + + printf(" 9 - Decoding the Sui Bech32 private key string into a 32 raw seed bytes...\n"); + uint8_t private_key_output[32]; + start_time = millis(); + int decoding_result = microsui_decode_bech32_private_key(private_key_output, private_key_bech32_output); + end_time = millis(); + + // Calculate the time taken for decoding + uint64_t time_taken_decoding = end_time - start_time; + if(decoding_result == 0) { + printf("\t Decoding successful.\n\n"); + } else { + printf("\t Decoding failed --\n"); + printf("\t Error code: %d -- Aborting\n\n", decoding_result); + return -1; + } + + + printf("\t MicroSui Benchmarks Results:\n"); + printf("\t Signatures:\n"); + printf("\t\t Time taken for signing from Sui Message: "); + if (time_taken_signature > 100000) { + printf("\t\t %llu ms\n", (unsigned long long)(time_taken_signature / 1000)); + } else { + printf("\t\t %.2f ms (%llu us) \n", (double)time_taken_signature / 1000.0, (unsigned long long)time_taken_signature); + } + + printf("\t\t Time taken for signing from precalculated digest: "); + if (time_taken_signature_from_digest > 100000) { + printf("\t %llu ms\n", (unsigned long long)(time_taken_signature_from_digest / 1000)); + } else { + printf("\t %.2f ms (%llu us) \n", (double)time_taken_signature_from_digest / 1000.0, (unsigned long long)time_taken_signature_from_digest); + } + + printf("\n\t Signature Check / Verification:\n"); + printf("\t\t Time taken for sign check from Sui Message: "); + if (time_taken_verification > 100000) { + printf("\t\t %llu ms\n", (unsigned long long)(time_taken_verification / 1000)); + } else { + printf("\t\t %.2f ms (%llu us) \n", (double)time_taken_verification / 1000.0, (unsigned long long)time_taken_verification); + } + + printf("\t\t Time taken for sign check from precalculated digest: "); + if (time_taken_verification_from_digest > 100000) { + printf("\t %llu ms\n", (unsigned long long)(time_taken_verification_from_digest / 1000)); + } else { + printf("\t %.2f ms (%llu us) \n", (double)time_taken_verification_from_digest / 1000.0, (unsigned long long)time_taken_verification_from_digest); + } + + printf("\n\t Key Management Operations:\n"); + printf("\t\t Time taken for deriving public key from seed: "); + if (time_taken_public_key_derivation > 100000) { + printf("\t\t %llu ms\n", (unsigned long long)(time_taken_public_key_derivation / 1000)); + } else { + printf("\t\t %.2f ms (%llu us) \n", (double)time_taken_public_key_derivation / 1000.0, (unsigned long long)time_taken_public_key_derivation); + } + + printf("\t\t Time taken for deriving keypair from seed: "); + if (time_taken_keypair_derivation > 100000) { + printf("\t\t %llu ms\n", (unsigned long long)(time_taken_keypair_derivation / 1000)); + } else { + printf("\t\t %.2f ms (%llu us) \n", (double)time_taken_keypair_derivation / 1000.0, (unsigned long long)time_taken_keypair_derivation); + } + + printf("\t\t Time taken for deriving Sui address from public key: "); + if (time_taken_address_derivation > 100000) { + printf("\t %llu ms\n", (unsigned long long)(time_taken_address_derivation / 1000)); + } else { + printf("\t %.2f ms (%llu us) \n", (double)time_taken_address_derivation / 1000.0, (unsigned long long)time_taken_address_derivation); + } + + printf("\n\t BECH32 Operations:\n"); + printf("\t\t Time taken for seed/private key bech32 encoding: "); + if (time_taken_encoding > 100000) { + printf("\t %llu ms\n", (unsigned long long)(time_taken_encoding / 1000)); + } else { + printf("\t %.2f ms (%llu us) \n", (double)time_taken_encoding / 1000.0, (unsigned long long)time_taken_encoding); + } + printf("\t\t Time taken for seed/private key bech32 decoding: "); + if (time_taken_decoding > 100000) { + printf("\t %llu ms\n", (unsigned long long)(time_taken_decoding / 1000)); + } else { + printf("\t %.2f ms (%llu us) \n", (double)time_taken_decoding / 1000.0, (unsigned long long)time_taken_decoding); + } + + printf("\n \tEnd of benchmarks.\n"); + + return 0; +} + +uint64_t millis(void) +{ +#if defined(_WIN32) || defined(_WIN64) + // Windows: QueryPerformanceCounter (high-resolution timer) + static LARGE_INTEGER freq = {0}; + LARGE_INTEGER now; + + if (freq.QuadPart == 0) + QueryPerformanceFrequency(&freq); + + QueryPerformanceCounter(&now); + return (uint64_t)((now.QuadPart * 1000000) / freq.QuadPart); + +#else + // Linux y macOS: clock_gettime(CLOCK_MONOTONIC) + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)(ts.tv_sec) + (ts.tv_nsec / 1000ULL); +#endif +} \ No newline at end of file diff --git a/examples/core_examples/benchmarks/benchmarks.c b/examples/core_examples/benchmarks/benchmarks.c deleted file mode 100644 index 2d8b95b..0000000 --- a/examples/core_examples/benchmarks/benchmarks.c +++ /dev/null @@ -1,95 +0,0 @@ -#include -#include -#include -#include - -#include "MicroSui.h" - -// Sui Message in hex format (this message must be signed) -const char* message_hex = "00000200080065cd1d0000000000202e3d52393c9035afd1ef38abd7fce2dad71f0e276b522fb274f4e14d1df974720202000101000001010300000000010100d79a4c7a655aa80cf92069bbac9666705f1d7181ff9c2d59efbc7e6ec4c3379d0180dc491e55e7caabfcdd1b0f538928d8d54107b9c1def3ed0baa3aa5106ba8674f0dd01400000000204b7e9da00f30cd1edf4d40710213c15a862e1fc175f2edb2b2c870c8559d65cdd79a4c7a655aa80cf92069bbac9666705f1d7181ff9c2d59efbc7e6ec4c3379de80300000000000040ab3c000000000000"; - -// You must place your seed private key (in bytes format) here. -const uint8_t private_key_seed[32] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -uint64_t millis(void); - -int main() { - printf("\n\t\t\t --- MicroSui Operations Benchmarks ---\n"); - printf("\n\tOriginal Message: %s", message_hex); - - // Converting the message from hex to bytes - size_t message_len = strlen(message_hex) / 2; // 2 hex chars = 1 byte - uint8_t message[message_len]; - hex_to_bytes(message_hex, message, message_len); - - // Generating the Sui Signature from the message and private key - uint8_t sui_sig[97]; - printf("\n\n\n Generating Signature...\n"); - - uint64_t start_time = millis(); - microsui_sign_ed25519(sui_sig, message, message_len, private_key_seed); - uint64_t end_time = millis(); - printf("\t Signature created successfully\n"); - - // Calculate the time taken for verification - uint64_t time_taken_signature = end_time - start_time; - - // Verifying the Sui Signature - start_time = millis(); - int verification_result = microsui_verify_signature(sui_sig, message, message_len); - end_time = millis(); - - // Calculate the time taken for verification - uint64_t time_taken_verification = end_time - start_time; - if(verification_result == 0) { - printf("\t Signature verified successfully\n\n"); - } else { - printf("\t Signature verification failed\n"); - printf("\t Error code: %d\n\n", verification_result); - } - - - printf("\t Benchmarks Results:\n"); - printf("\t\t Time taken for signing: "); - if (time_taken_signature > 100000) { - printf("%llu ms\n", (unsigned long long)(time_taken_signature / 1000)); - } else { - printf("%.2f ms (%llu us) \n", (double)time_taken_signature / 1000.0, (unsigned long long)time_taken_signature); - } - - printf("\t\t Time taken for verification: "); - if (time_taken_verification > 100000) { - printf("%llu ms\n", (unsigned long long)(time_taken_verification / 1000)); - } else { - printf("%.2f ms (%llu us) \n", (double)time_taken_verification / 1000.0, (unsigned long long)time_taken_verification); - } - printf("\n End of benchmarks.\n"); - - return 0; -} - -uint64_t millis(void) -{ -#if defined(_WIN32) || defined(_WIN64) - // Windows: QueryPerformanceCounter (high-resolution timer) - static LARGE_INTEGER freq = {0}; - LARGE_INTEGER now; - - if (freq.QuadPart == 0) - QueryPerformanceFrequency(&freq); - - QueryPerformanceCounter(&now); - return (uint64_t)((now.QuadPart * 1000000) / freq.QuadPart); - -#else - // Linux y macOS: clock_gettime(CLOCK_MONOTONIC) - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (uint64_t)(ts.tv_sec) + (ts.tv_nsec / 1000ULL); -#endif -} \ No newline at end of file diff --git a/library.json b/library.json index 136a198..3ee0655 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "microsui-lib", - "version": "0.3.3", + "version": "0.3.4", "description": "MicroSui Library is a lightweight C library for embedded systems to interact with the Sui Network Blockchain", "keywords": "microsui, sui, blockchain, sui-network, walrus, cryptography, c-library", "repository": { diff --git a/library.properties b/library.properties index 76cd5f4..c918d01 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=MicroSui -version=0.3.3 +version=0.3.4 author=Gustavo Belbruno maintainer=Gustavo Belbruno sentence=Pure C library for interacting with the Sui Network blockchain from microcontrollers. diff --git a/src/microsui_core/key_management.c b/src/microsui_core/key_management.c index 836497b..2608c3d 100644 --- a/src/microsui_core/key_management.c +++ b/src/microsui_core/key_management.c @@ -62,6 +62,39 @@ int microsui_derive_public_key_ed25519(uint8_t public_key_out[32], const uint8_t // Clear sensitive data from memory crypto_wipe(seed_cp, sizeof seed_cp); crypto_wipe(_, sizeof _); - + return 0; -} \ No newline at end of file +} + +/** + * @brief Derive an Ed25519 key pair (64-byte secret key and 32-byte public key) from a 32-byte seed. + * + * Generates the full 64-byte Ed25519 secret key and its corresponding + * 32-byte public key from the provided seed. + * + * @param[out] secret_key_out Output buffer for the 64-byte Ed25519 secret key. + * @param[out] public_key_out Output buffer for the 32-byte Ed25519 public key. + * @param[in] seed 32-byte Ed25519 seed. + * + * @return 0 on success; -1 if input pointers are NULL. + * + * @note The seed is copied to a local buffer before use, + * as the underlying library may modify it during key generation. + * @note The caller is responsible for wiping secret_key_out when no longer needed. + */ +int microsui_derive_keypair_ed25519(uint8_t secret_key_out[64], uint8_t public_key_out[32], const uint8_t seed[32]) { + if (seed == NULL || secret_key_out == NULL || public_key_out == NULL) return -1; + + // Copy seed to a local variable + uint8_t seed_cp[32]; + memcpy(seed_cp, seed, 32); + + // Generate secret and public key from seed using Ed25519 + crypto_ed25519_key_pair(secret_key_out, public_key_out, seed_cp); + + // Clear sensitive data from memory + crypto_wipe(seed_cp, sizeof seed_cp); + + return 0; +} + diff --git a/src/microsui_core/key_management.h b/src/microsui_core/key_management.h index 67146f0..0234bfc 100644 --- a/src/microsui_core/key_management.h +++ b/src/microsui_core/key_management.h @@ -8,4 +8,6 @@ int microsui_derive_sui_address_ed25519(uint8_t sui_address_out[32], const uint8 int microsui_derive_public_key_ed25519(uint8_t public_key_out[32], const uint8_t seed[32]); +int microsui_derive_keypair_ed25519(uint8_t secret_key_out[64], uint8_t public_key_out[32], const uint8_t seed[32]); + #endif \ No newline at end of file diff --git a/src/microsui_core/sign.c b/src/microsui_core/sign.c index 8869b7b..098251c 100644 --- a/src/microsui_core/sign.c +++ b/src/microsui_core/sign.c @@ -4,11 +4,31 @@ #include #include #include -#include #include "lib/monocypher/monocypher.h" #include "byte_conversions.h" +/** + * @brief Sign a pre-computed digest and build a Sui-formatted Ed25519 signature. + * + * Signs the given 32-byte digest using the provided Ed25519 secret key and + * assembles the result into the Sui signature format: + * [0x00 scheme | 64-byte Ed25519 signature | 32-byte public key] + * + * @param[out] sui_sig_out Output buffer for the Sui signature (must be 97 bytes). + * @param[in] digest 32-byte digest to sign. + * @param[in] secret_key 64-byte Ed25519 secret key, as produced by crypto_ed25519_key_pair (dont confuse with the 32-byte seed). + * @param[in] public_key 32-byte Ed25519 public key. + */ +void microsui_sign_ed25519_from_digest(uint8_t sui_sig_out[97], const uint8_t digest[32], const uint8_t secret_key[64], const uint8_t public_key[32]) { + uint8_t ed25519_signature[64]; + crypto_ed25519_sign_sha512(ed25519_signature, secret_key, digest, 32); + + sui_sig_out[0] = 0x00; // Ed25519 Scheme + memcpy(sui_sig_out + 1, ed25519_signature, 64); + memcpy(sui_sig_out + 65, public_key, 32); +} + /** * @brief Sign a Sui Transaction message using Ed25519 and produce a Sui-formatted signature. * @@ -49,14 +69,8 @@ int microsui_sign_ed25519(uint8_t sui_sig_out[97], const uint8_t* message, const crypto_blake2b_final(&ctx, digest); - // 4. Sign the digest using Ed25519 with the secret key and public key - uint8_t ed25519_signature[64]; - crypto_ed25519_sign_sha512(ed25519_signature, secret_key, digest, 32); - - // 5. Build Sui signature - sui_sig_out[0] = 0x00; // Ed25519 Scheme - memcpy(sui_sig_out + 1, ed25519_signature, 64); - memcpy(sui_sig_out + 65, public_key, 32); + // 4. Sign the digest and build the Sui signature + microsui_sign_ed25519_from_digest(sui_sig_out, digest, secret_key, public_key); crypto_wipe(secret_key, sizeof secret_key); crypto_wipe(seed_cp, sizeof seed_cp); @@ -66,6 +80,49 @@ int microsui_sign_ed25519(uint8_t sui_sig_out[97], const uint8_t* message, const return 0; } +/** + * @brief Sign a Sui Transaction message using pre-derived Ed25519 keys. + * This function is a variant of microsui_sign_ed25519(), the purpose of which is skipping the key pair + * derivation step which is an expensive part of the signing process. This makes it suitable for low-power devices + * where CPU time is critical and the caller can manage key pairs separately for better performance. + * + * Builds the "message with intent" (prefix + tx bytes), digests it with BLAKE2b, + * signs the digest with Ed25519, and encodes the result in the Sui signature + * format (scheme byte + 64-byte Ed25519 signature + 32-byte public key). + * + * Unlike microsui_sign_ed25519(), this function skips key pair derivation, + * which is the an expensive step (~50% of the total signing time). + * The caller is responsible for providing a valid key pair, which can be calculated once + * from microsui_derive_keypair_ed25519() function and reused across multiple signing operations. + * + * @param[out] sui_sig_out Output buffer for the Sui signature (must be 97 bytes). + * @param[in] message Pointer to raw transaction bytes (already serialized). + * @param[in] message_len Length of the transaction bytes. + * @param[in] secret_key 64-byte Ed25519 secret key, as produced by crypto_ed25519_key_pair (dont confuse with the 32-byte seed). + * @param[in] public_key 32-byte Ed25519 public key. + * + * @warning No validation is performed to check that the public key corresponds + * to the secret key. Passing mismatched keys will produce an invalid signature. + * It's up to the caller to ensure that the provided key pair is correct and corresponds to the intended signing identity. + */ +void microsui_sign_ed25519_with_keys(uint8_t sui_sig_out[97], const uint8_t* message, const size_t message_len, const uint8_t secret_key[64], const uint8_t public_key[32]) { + uint8_t digest[32]; + + crypto_blake2b_ctx ctx; + crypto_blake2b_init(&ctx, 32); + + const uint8_t intent[3] = {0x00, 0x00, 0x00}; + crypto_blake2b_update(&ctx, intent, sizeof intent); + crypto_blake2b_update(&ctx, message, message_len); + + crypto_blake2b_final(&ctx, digest); + + microsui_sign_ed25519_from_digest(sui_sig_out, digest, secret_key, public_key); + + crypto_wipe(&ctx, sizeof ctx); + crypto_wipe(digest, sizeof digest); +} + /** * @brief Generic signing entry point for multiple signature schemes. * @@ -152,7 +209,46 @@ int microsui_verify_signature_ed25519(uint8_t sui_sig[97], const uint8_t* messag if(crypto_ed25519_check_sha512(signature, public_key, digest, 32) != 0) { return -2; // Signature verification failed } - + + return 0; // Signature is valid +} + +/** + * @brief Verify a Sui-formatted Ed25519 signature against a pre-computed digest. + * + * Expects a Sui signature encoded as: + * [0x00 scheme | 64-byte Ed25519 signature | 32-byte public key] + * + * Extracts the Ed25519 signature and public key from the Sui signature payload, + * then verifies them against the caller-supplied 32-byte digest. + * + * @param[in] sui_sig Pointer to the Sui signature buffer (must be 97 bytes). + * @param[in] digest 32-byte pre-computed digest to verify against. + * + * @return 0 if the signature is valid, + * -1 if the scheme byte is not Ed25519 (0x00), + * -2 if the signature verification fails. + * + * @note This function does not compute the digest internally; it expects + * the caller to provide a ready-to-verify 32-byte digest. + * @note This function does not recover a public key; it verifies using the public key + * embedded inside the Sui signature payload. + */ +int microsui_verify_signature_ed25519_from_digest(uint8_t sui_sig[97], const uint8_t digest[32]) { + if(sui_sig[0] != 0x00) { + return -1; // This is not an Ed25519 signature + } + + uint8_t signature[64]; + memcpy(signature, sui_sig + 1, 64); + + uint8_t public_key[32]; + memcpy(public_key, sui_sig + 65, 32); + + if(crypto_ed25519_check_sha512(signature, public_key, digest, 32) != 0) { + return -2; // Signature verification failed + } + return 0; // Signature is valid } @@ -218,6 +314,60 @@ int microsui_verify_signature_ed25519_with_public_key(uint8_t sui_sig[97], const return 0; // Signature is valid and public key matches } +/** + * @brief Verify a Sui-formatted Ed25519 signature against a pre-computed digest + * and validate it against a known public key. + * + * Expects a Sui signature encoded as: + * [0x00 scheme | 64-byte Ed25519 signature | 32-byte public key] + * + * Extracts the Ed25519 signature and public key from the Sui signature payload, + * verifies them against the caller-supplied 32-byte digest, and then confirms + * that the embedded public key matches the caller-supplied key using a + * constant-time comparison. + * + * @param[in] sui_sig Pointer to the Sui signature buffer (must be 97 bytes). + * @param[in] digest 32-byte pre-computed digest to verify against. + * @param[in] public_key Pointer to the expected 32-byte Ed25519 public key. + * + * @return 0 if the signature is valid and the embedded public key matches @p public_key, + * -1 if the scheme byte is not Ed25519 (0x00), + * -2 if the signature verification fails, + * -3 if the public key embedded in the signature does not match @p public_key. + * + * @note This function does not compute the digest internally; it expects + * the caller to provide a ready-to-verify 32-byte digest. + * @note The public key comparison is performed in constant time to mitigate timing attacks. + * @note Signature verification (-2) is checked before public key comparison (-3); a -3 result + * therefore implies the signature itself was cryptographically valid. + */ +int microsui_verify_signature_ed25519_with_public_key_from_digest(uint8_t sui_sig[97], const uint8_t digest[32], uint8_t public_key[32]) { + if(sui_sig[0] != 0x00) { + return -1; // This is not an Ed25519 signature + } + + uint8_t signature[64]; + memcpy(signature, sui_sig + 1, 64); + + uint8_t signature_public_key[32]; + memcpy(signature_public_key, sui_sig + 65, 32); + + if(crypto_ed25519_check_sha512(signature, signature_public_key, digest, 32) != 0) { + return -2; // Signature verification failed + } + + // Check if the public key in the signature matches the provided public key (with timing attacks protection) + uint8_t diff = 0; + for (size_t i = 0; i < 32; i++) { + diff |= signature_public_key[i] ^ public_key[i]; + } + if (diff != 0) { + return -3; // Public key mismatch + } + + return 0; // Signature is valid and public key matches +} + /** * @brief Generic signature verification entry point for multiple signature schemes. * diff --git a/src/microsui_core/sign.h b/src/microsui_core/sign.h index 26fbbdc..d238283 100644 --- a/src/microsui_core/sign.h +++ b/src/microsui_core/sign.h @@ -8,10 +8,18 @@ int microsui_sign(uint8_t sui_sig_out[97], uint8_t scheme, const uint8_t* messag int microsui_sign_ed25519(uint8_t sui_sig_out[97], const uint8_t* message, const size_t message_len, const uint8_t seed[32]); +void microsui_sign_ed25519_with_keys(uint8_t sui_sig_out[97], const uint8_t* message, const size_t message_len, const uint8_t secret_key[64], const uint8_t public_key[32]); + +void microsui_sign_ed25519_from_digest(uint8_t sui_sig_out[97], const uint8_t digest[32], const uint8_t secret_key[64], const uint8_t public_key[32]); + int microsui_verify_signature_ed25519(uint8_t sui_sig[97], const uint8_t* message, const size_t message_len); +int microsui_verify_signature_ed25519_from_digest(uint8_t sui_sig[97], const uint8_t digest[32]); + int microsui_verify_signature_ed25519_with_public_key(uint8_t sui_sig[97], const uint8_t* message, const size_t message_len, uint8_t public_key[32]); +int microsui_verify_signature_ed25519_with_public_key_from_digest(uint8_t sui_sig[97], const uint8_t digest[32], uint8_t public_key[32]); + int microsui_verify_signature(uint8_t sui_sig[97], const uint8_t* message, const size_t message_len); int microsui_verify_signature_with_public_key(uint8_t sui_sig[97], const uint8_t* message, const size_t message_len, uint8_t public_key[32]);