From 08a55aa80e1306a9e0f7b145af11191681739b8b Mon Sep 17 00:00:00 2001 From: Gulshan Yadav Date: Wed, 28 Jan 2026 13:47:55 +0530 Subject: [PATCH] feat(sdk): Add Crypto SDK for all 12 languages Implements quantum-resistant cryptographic primitives across all SDK languages: - Hybrid Ed25519 + Dilithium3 signatures (classical + post-quantum) - BIP-39 mnemonic support (12, 15, 18, 21, 24 words) - BIP-44 hierarchical key derivation (coin type 0x5359) - Post-quantum algorithms: Falcon (FIPS 206), SPHINCS+ (FIPS 205) - Key derivation: HKDF-SHA3-256, PBKDF2 - Hash functions: SHA3-256, BLAKE3, Keccak-256 Languages: JavaScript/TypeScript, Python, Go, Rust, Flutter/Dart, Java, Kotlin, Swift, C, C++, C#/.NET, Ruby --- sdk/c/include/synor_crypto.h | 799 ++++++++++++++++++ sdk/cpp/include/synor/crypto.hpp | 524 ++++++++++++ sdk/csharp/src/Synor.Crypto/SynorCrypto.cs | 336 ++++++++ sdk/csharp/src/Synor.Crypto/Types.cs | 505 +++++++++++ sdk/flutter/lib/src/crypto/client.dart | 466 ++++++++++ sdk/flutter/lib/src/crypto/types.dart | 658 +++++++++++++++ sdk/go/crypto/client.go | 691 +++++++++++++++ sdk/go/crypto/types.go | 479 +++++++++++ .../java/io/synor/crypto/SynorCrypto.java | 351 ++++++++ .../src/main/java/io/synor/crypto/Types.java | 404 +++++++++ sdk/js/src/crypto/index.ts | 767 +++++++++++++++++ sdk/js/src/crypto/types.ts | 370 ++++++++ .../kotlin/io/synor/crypto/SynorCrypto.kt | 290 +++++++ .../src/main/kotlin/io/synor/crypto/Types.kt | 353 ++++++++ sdk/python/src/synor_crypto/__init__.py | 173 ++++ sdk/python/src/synor_crypto/client.py | 552 ++++++++++++ sdk/python/src/synor_crypto/types.py | 526 ++++++++++++ sdk/ruby/lib/synor/crypto.rb | 13 + sdk/ruby/lib/synor/crypto/client.rb | 334 ++++++++ sdk/ruby/lib/synor/crypto/types.rb | 576 +++++++++++++ sdk/rust/src/crypto/client.rs | 673 +++++++++++++++ sdk/rust/src/crypto/mod.rs | 42 + sdk/rust/src/crypto/types.rs | 537 ++++++++++++ .../Sources/SynorCrypto/SynorCrypto.swift | 347 ++++++++ sdk/swift/Sources/SynorCrypto/Types.swift | 499 +++++++++++ 25 files changed, 11265 insertions(+) create mode 100644 sdk/c/include/synor_crypto.h create mode 100644 sdk/cpp/include/synor/crypto.hpp create mode 100644 sdk/csharp/src/Synor.Crypto/SynorCrypto.cs create mode 100644 sdk/csharp/src/Synor.Crypto/Types.cs create mode 100644 sdk/flutter/lib/src/crypto/client.dart create mode 100644 sdk/flutter/lib/src/crypto/types.dart create mode 100644 sdk/go/crypto/client.go create mode 100644 sdk/go/crypto/types.go create mode 100644 sdk/java/src/main/java/io/synor/crypto/SynorCrypto.java create mode 100644 sdk/java/src/main/java/io/synor/crypto/Types.java create mode 100644 sdk/js/src/crypto/index.ts create mode 100644 sdk/js/src/crypto/types.ts create mode 100644 sdk/kotlin/src/main/kotlin/io/synor/crypto/SynorCrypto.kt create mode 100644 sdk/kotlin/src/main/kotlin/io/synor/crypto/Types.kt create mode 100644 sdk/python/src/synor_crypto/__init__.py create mode 100644 sdk/python/src/synor_crypto/client.py create mode 100644 sdk/python/src/synor_crypto/types.py create mode 100644 sdk/ruby/lib/synor/crypto.rb create mode 100644 sdk/ruby/lib/synor/crypto/client.rb create mode 100644 sdk/ruby/lib/synor/crypto/types.rb create mode 100644 sdk/rust/src/crypto/client.rs create mode 100644 sdk/rust/src/crypto/mod.rs create mode 100644 sdk/rust/src/crypto/types.rs create mode 100644 sdk/swift/Sources/SynorCrypto/SynorCrypto.swift create mode 100644 sdk/swift/Sources/SynorCrypto/Types.swift diff --git a/sdk/c/include/synor_crypto.h b/sdk/c/include/synor_crypto.h new file mode 100644 index 0000000..c9059a1 --- /dev/null +++ b/sdk/c/include/synor_crypto.h @@ -0,0 +1,799 @@ +/** + * Synor Crypto SDK for C + * + * Quantum-resistant cryptographic primitives for the Synor blockchain. + * + * Example: + * ```c + * synor_crypto_config_t config = { + * .api_key = "your-api-key", + * .endpoint = "https://crypto.synor.io/v1", + * .timeout = 30000, + * .retries = 3 + * }; + * + * synor_crypto_t* crypto = synor_crypto_create(&config); + * + * // Generate a mnemonic + * synor_mnemonic_t mnemonic; + * synor_crypto_mnemonic_generate(crypto, 24, &mnemonic); + * printf("Backup words: %s\n", mnemonic.phrase); + * + * // Create keypair from mnemonic + * synor_hybrid_keypair_t keypair; + * synor_crypto_keypair_from_mnemonic(crypto, mnemonic.phrase, "", &keypair); + * + * synor_crypto_destroy(crypto); + * ``` + */ + +#ifndef SYNOR_CRYPTO_H +#define SYNOR_CRYPTO_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ============================================================================ + * Constants + * ============================================================================ */ + +#define SYNOR_CRYPTO_ED25519_PUBLIC_KEY_SIZE 32 +#define SYNOR_CRYPTO_ED25519_SECRET_KEY_SIZE 32 +#define SYNOR_CRYPTO_ED25519_SIGNATURE_SIZE 64 +#define SYNOR_CRYPTO_DILITHIUM3_PUBLIC_KEY_SIZE 1952 +#define SYNOR_CRYPTO_DILITHIUM3_SIGNATURE_SIZE 3293 +#define SYNOR_CRYPTO_HYBRID_SIGNATURE_SIZE (64 + 3293) +#define SYNOR_CRYPTO_COIN_TYPE 0x5359 +#define SYNOR_CRYPTO_MIN_PBKDF2_ITERATIONS 10000 +#define SYNOR_CRYPTO_MIN_SALT_LENGTH 8 +#define SYNOR_CRYPTO_DEFAULT_ENDPOINT "https://crypto.synor.io/v1" + +#define SYNOR_CRYPTO_MAX_PHRASE_LEN 1024 +#define SYNOR_CRYPTO_MAX_ADDRESS_LEN 128 +#define SYNOR_CRYPTO_MAX_HASH_LEN 64 +#define SYNOR_CRYPTO_MAX_ERROR_LEN 256 + +/* ============================================================================ + * Enumerations + * ============================================================================ */ + +typedef enum { + SYNOR_NETWORK_MAINNET = 0, + SYNOR_NETWORK_TESTNET = 1, + SYNOR_NETWORK_DEVNET = 2 +} synor_network_t; + +typedef enum { + SYNOR_FALCON_512 = 0, + SYNOR_FALCON_1024 = 1 +} synor_falcon_variant_t; + +typedef enum { + SYNOR_SPHINCS_SHAKE128S = 0, + SYNOR_SPHINCS_SHAKE192S = 1, + SYNOR_SPHINCS_SHAKE256S = 2 +} synor_sphincs_variant_t; + +typedef enum { + SYNOR_PQ_DILITHIUM3 = 0, + SYNOR_PQ_FALCON512 = 1, + SYNOR_PQ_FALCON1024 = 2, + SYNOR_PQ_SPHINCS128S = 3, + SYNOR_PQ_SPHINCS192S = 4, + SYNOR_PQ_SPHINCS256S = 5 +} synor_pq_algorithm_t; + +typedef enum { + SYNOR_ALGO_FAMILY_CLASSICAL = 0, + SYNOR_ALGO_FAMILY_LATTICE = 1, + SYNOR_ALGO_FAMILY_HASH_BASED = 2, + SYNOR_ALGO_FAMILY_HYBRID = 3 +} synor_algorithm_family_t; + +typedef enum { + SYNOR_OK = 0, + SYNOR_ERROR_INVALID_PARAMS = -1, + SYNOR_ERROR_NETWORK = -2, + SYNOR_ERROR_AUTH = -3, + SYNOR_ERROR_TIMEOUT = -4, + SYNOR_ERROR_API = -5, + SYNOR_ERROR_CLOSED = -6, + SYNOR_ERROR_MEMORY = -7, + SYNOR_ERROR_PARSE = -8 +} synor_error_t; + +/* ============================================================================ + * Configuration Types + * ============================================================================ */ + +typedef struct { + const char* api_key; + const char* endpoint; + uint32_t timeout; + uint32_t retries; + bool debug; + synor_network_t default_network; +} synor_crypto_config_t; + +typedef struct { + uint8_t* salt; + size_t salt_len; + uint8_t* info; + size_t info_len; + uint32_t output_length; +} synor_derivation_config_t; + +typedef struct { + uint8_t* salt; + size_t salt_len; + uint32_t iterations; + uint32_t output_length; +} synor_password_derivation_config_t; + +typedef struct { + uint32_t account; + uint32_t change; + uint32_t index; +} synor_derivation_path_t; + +/* ============================================================================ + * Key Types + * ============================================================================ */ + +typedef struct { + char ed25519[88]; /* Base64 encoded */ + char dilithium[2608]; /* Base64 encoded */ +} synor_hybrid_public_key_t; + +typedef struct { + char ed25519_seed[88]; /* Base64 encoded */ + char master_seed[88]; /* Base64 encoded */ +} synor_secret_key_t; + +typedef struct { + synor_falcon_variant_t variant; + uint8_t* bytes; + size_t bytes_len; +} synor_falcon_public_key_t; + +typedef struct { + synor_falcon_variant_t variant; + uint8_t* bytes; + size_t bytes_len; +} synor_falcon_secret_key_t; + +typedef struct { + synor_sphincs_variant_t variant; + uint8_t* bytes; + size_t bytes_len; +} synor_sphincs_public_key_t; + +typedef struct { + synor_sphincs_variant_t variant; + uint8_t* bytes; + size_t bytes_len; +} synor_sphincs_secret_key_t; + +/* ============================================================================ + * Signature Types + * ============================================================================ */ + +typedef struct { + char ed25519[88]; /* Base64 encoded */ + char dilithium[4392]; /* Base64 encoded */ +} synor_hybrid_signature_t; + +typedef struct { + synor_falcon_variant_t variant; + uint8_t* signature; + size_t signature_len; +} synor_falcon_signature_t; + +typedef struct { + synor_sphincs_variant_t variant; + uint8_t* signature; + size_t signature_len; +} synor_sphincs_signature_t; + +/* ============================================================================ + * Keypair Types + * ============================================================================ */ + +typedef struct { + synor_hybrid_public_key_t public_key; + synor_secret_key_t secret_key; + char addresses[3][SYNOR_CRYPTO_MAX_ADDRESS_LEN]; /* mainnet, testnet, devnet */ +} synor_hybrid_keypair_t; + +typedef struct { + synor_falcon_variant_t variant; + synor_falcon_public_key_t public_key; + synor_falcon_secret_key_t secret_key; +} synor_falcon_keypair_t; + +typedef struct { + synor_sphincs_variant_t variant; + synor_sphincs_public_key_t public_key; + synor_sphincs_secret_key_t secret_key; +} synor_sphincs_keypair_t; + +/* ============================================================================ + * Mnemonic Types + * ============================================================================ */ + +typedef struct { + char phrase[SYNOR_CRYPTO_MAX_PHRASE_LEN]; + char** words; + size_t word_count; + uint8_t* entropy; + size_t entropy_len; +} synor_mnemonic_t; + +typedef struct { + bool valid; + char error[SYNOR_CRYPTO_MAX_ERROR_LEN]; +} synor_mnemonic_validation_t; + +/* ============================================================================ + * Address Types + * ============================================================================ */ + +typedef struct { + char address[SYNOR_CRYPTO_MAX_ADDRESS_LEN]; + synor_network_t network; + uint8_t pubkey_hash[32]; +} synor_address_t; + +/* ============================================================================ + * Hash Types + * ============================================================================ */ + +typedef struct { + char hex[SYNOR_CRYPTO_MAX_HASH_LEN + 1]; + uint8_t bytes[32]; +} synor_hash256_t; + +/* ============================================================================ + * Client Handle + * ============================================================================ */ + +typedef struct synor_crypto_s synor_crypto_t; + +/* ============================================================================ + * Client Lifecycle + * ============================================================================ */ + +/** + * Create a new Synor Crypto client. + * + * @param config Configuration for the client + * @return Pointer to the client handle, or NULL on error + */ +synor_crypto_t* synor_crypto_create(const synor_crypto_config_t* config); + +/** + * Destroy a Synor Crypto client and free resources. + * + * @param crypto Client handle + */ +void synor_crypto_destroy(synor_crypto_t* crypto); + +/** + * Check if the client has been closed. + * + * @param crypto Client handle + * @return true if closed, false otherwise + */ +bool synor_crypto_is_closed(const synor_crypto_t* crypto); + +/** + * Perform a health check on the API. + * + * @param crypto Client handle + * @return SYNOR_OK if healthy, error code otherwise + */ +synor_error_t synor_crypto_health_check(synor_crypto_t* crypto); + +/* ============================================================================ + * Mnemonic Operations + * ============================================================================ */ + +/** + * Generate a new mnemonic phrase. + * + * @param crypto Client handle + * @param word_count Number of words (12, 15, 18, 21, or 24) + * @param out Output mnemonic + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_mnemonic_generate( + synor_crypto_t* crypto, + uint32_t word_count, + synor_mnemonic_t* out +); + +/** + * Parse an existing mnemonic phrase. + * + * @param crypto Client handle + * @param phrase Mnemonic phrase + * @param out Output mnemonic + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_mnemonic_from_phrase( + synor_crypto_t* crypto, + const char* phrase, + synor_mnemonic_t* out +); + +/** + * Validate a mnemonic phrase. + * + * @param crypto Client handle + * @param phrase Mnemonic phrase to validate + * @param out Validation result + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_mnemonic_validate( + synor_crypto_t* crypto, + const char* phrase, + synor_mnemonic_validation_t* out +); + +/** + * Convert a mnemonic phrase to a seed. + * + * @param crypto Client handle + * @param phrase Mnemonic phrase + * @param passphrase Optional passphrase (can be empty string) + * @param seed Output seed buffer (must be at least 64 bytes) + * @param seed_len Output seed length + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_mnemonic_to_seed( + synor_crypto_t* crypto, + const char* phrase, + const char* passphrase, + uint8_t* seed, + size_t* seed_len +); + +/** + * Free mnemonic resources. + * + * @param mnemonic Mnemonic to free + */ +void synor_crypto_mnemonic_free(synor_mnemonic_t* mnemonic); + +/* ============================================================================ + * Keypair Operations + * ============================================================================ */ + +/** + * Generate a new hybrid keypair. + * + * @param crypto Client handle + * @param out Output keypair + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_keypair_generate( + synor_crypto_t* crypto, + synor_hybrid_keypair_t* out +); + +/** + * Create a keypair from a mnemonic phrase. + * + * @param crypto Client handle + * @param phrase Mnemonic phrase + * @param passphrase Optional passphrase (can be empty string) + * @param out Output keypair + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_keypair_from_mnemonic( + synor_crypto_t* crypto, + const char* phrase, + const char* passphrase, + synor_hybrid_keypair_t* out +); + +/** + * Create a keypair from a seed. + * + * @param crypto Client handle + * @param seed Seed bytes + * @param seed_len Seed length + * @param out Output keypair + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_keypair_from_seed( + synor_crypto_t* crypto, + const uint8_t* seed, + size_t seed_len, + synor_hybrid_keypair_t* out +); + +/** + * Get an address for a public key. + * + * @param crypto Client handle + * @param public_key Public key + * @param network Network type + * @param out Output address + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_keypair_get_address( + synor_crypto_t* crypto, + const synor_hybrid_public_key_t* public_key, + synor_network_t network, + synor_address_t* out +); + +/* ============================================================================ + * Signing Operations + * ============================================================================ */ + +/** + * Sign a message with a hybrid keypair. + * + * @param crypto Client handle + * @param keypair Keypair for signing + * @param message Message to sign + * @param message_len Message length + * @param out Output signature + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_sign( + synor_crypto_t* crypto, + const synor_hybrid_keypair_t* keypair, + const uint8_t* message, + size_t message_len, + synor_hybrid_signature_t* out +); + +/** + * Verify a hybrid signature. + * + * @param crypto Client handle + * @param public_key Public key for verification + * @param message Original message + * @param message_len Message length + * @param signature Signature to verify + * @param valid Output: true if valid, false otherwise + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_verify( + synor_crypto_t* crypto, + const synor_hybrid_public_key_t* public_key, + const uint8_t* message, + size_t message_len, + const synor_hybrid_signature_t* signature, + bool* valid +); + +/** + * Sign a message with Ed25519. + * + * @param crypto Client handle + * @param secret_key Ed25519 secret key (32 bytes) + * @param message Message to sign + * @param message_len Message length + * @param signature Output signature (64 bytes) + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_sign_ed25519( + synor_crypto_t* crypto, + const uint8_t* secret_key, + const uint8_t* message, + size_t message_len, + uint8_t* signature +); + +/* ============================================================================ + * Falcon Operations + * ============================================================================ */ + +/** + * Generate a Falcon keypair. + * + * @param crypto Client handle + * @param variant Falcon variant (512 or 1024) + * @param out Output keypair + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_falcon_generate( + synor_crypto_t* crypto, + synor_falcon_variant_t variant, + synor_falcon_keypair_t* out +); + +/** + * Sign a message with Falcon. + * + * @param crypto Client handle + * @param keypair Falcon keypair + * @param message Message to sign + * @param message_len Message length + * @param out Output signature + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_falcon_sign( + synor_crypto_t* crypto, + const synor_falcon_keypair_t* keypair, + const uint8_t* message, + size_t message_len, + synor_falcon_signature_t* out +); + +/** + * Verify a Falcon signature. + * + * @param crypto Client handle + * @param public_key Falcon public key + * @param public_key_len Public key length + * @param message Original message + * @param message_len Message length + * @param signature Signature to verify + * @param valid Output: true if valid, false otherwise + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_falcon_verify( + synor_crypto_t* crypto, + const uint8_t* public_key, + size_t public_key_len, + const uint8_t* message, + size_t message_len, + const synor_falcon_signature_t* signature, + bool* valid +); + +/** + * Free Falcon keypair resources. + * + * @param keypair Keypair to free + */ +void synor_crypto_falcon_keypair_free(synor_falcon_keypair_t* keypair); + +/** + * Free Falcon signature resources. + * + * @param signature Signature to free + */ +void synor_crypto_falcon_signature_free(synor_falcon_signature_t* signature); + +/* ============================================================================ + * SPHINCS+ Operations + * ============================================================================ */ + +/** + * Generate a SPHINCS+ keypair. + * + * @param crypto Client handle + * @param variant SPHINCS+ variant + * @param out Output keypair + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_sphincs_generate( + synor_crypto_t* crypto, + synor_sphincs_variant_t variant, + synor_sphincs_keypair_t* out +); + +/** + * Sign a message with SPHINCS+. + * + * @param crypto Client handle + * @param keypair SPHINCS+ keypair + * @param message Message to sign + * @param message_len Message length + * @param out Output signature + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_sphincs_sign( + synor_crypto_t* crypto, + const synor_sphincs_keypair_t* keypair, + const uint8_t* message, + size_t message_len, + synor_sphincs_signature_t* out +); + +/** + * Verify a SPHINCS+ signature. + * + * @param crypto Client handle + * @param public_key SPHINCS+ public key + * @param public_key_len Public key length + * @param message Original message + * @param message_len Message length + * @param signature Signature to verify + * @param valid Output: true if valid, false otherwise + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_sphincs_verify( + synor_crypto_t* crypto, + const uint8_t* public_key, + size_t public_key_len, + const uint8_t* message, + size_t message_len, + const synor_sphincs_signature_t* signature, + bool* valid +); + +/** + * Free SPHINCS+ keypair resources. + * + * @param keypair Keypair to free + */ +void synor_crypto_sphincs_keypair_free(synor_sphincs_keypair_t* keypair); + +/** + * Free SPHINCS+ signature resources. + * + * @param signature Signature to free + */ +void synor_crypto_sphincs_signature_free(synor_sphincs_signature_t* signature); + +/* ============================================================================ + * KDF Operations + * ============================================================================ */ + +/** + * Derive a key using HKDF. + * + * @param crypto Client handle + * @param seed Input seed + * @param seed_len Seed length + * @param config Derivation configuration + * @param key Output key buffer + * @param key_len Output key length + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_kdf_derive( + synor_crypto_t* crypto, + const uint8_t* seed, + size_t seed_len, + const synor_derivation_config_t* config, + uint8_t* key, + size_t* key_len +); + +/** + * Derive a key from a password using PBKDF2. + * + * @param crypto Client handle + * @param password Password bytes + * @param password_len Password length + * @param config Password derivation configuration + * @param key Output key buffer + * @param key_len Output key length + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_kdf_derive_password( + synor_crypto_t* crypto, + const uint8_t* password, + size_t password_len, + const synor_password_derivation_config_t* config, + uint8_t* key, + size_t* key_len +); + +/* ============================================================================ + * Hash Operations + * ============================================================================ */ + +/** + * Compute SHA3-256 hash. + * + * @param crypto Client handle + * @param data Input data + * @param data_len Data length + * @param out Output hash + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_hash_sha3_256( + synor_crypto_t* crypto, + const uint8_t* data, + size_t data_len, + synor_hash256_t* out +); + +/** + * Compute BLAKE3 hash. + * + * @param crypto Client handle + * @param data Input data + * @param data_len Data length + * @param out Output hash + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_hash_blake3( + synor_crypto_t* crypto, + const uint8_t* data, + size_t data_len, + synor_hash256_t* out +); + +/** + * Compute Keccak-256 hash. + * + * @param crypto Client handle + * @param data Input data + * @param data_len Data length + * @param out Output hash + * @return SYNOR_OK on success, error code otherwise + */ +synor_error_t synor_crypto_hash_keccak256( + synor_crypto_t* crypto, + const uint8_t* data, + size_t data_len, + synor_hash256_t* out +); + +/* ============================================================================ + * Utility Functions + * ============================================================================ */ + +/** + * Get the signature size for a Falcon variant. + * + * @param variant Falcon variant + * @return Signature size in bytes + */ +size_t synor_crypto_falcon_signature_size(synor_falcon_variant_t variant); + +/** + * Get the public key size for a Falcon variant. + * + * @param variant Falcon variant + * @return Public key size in bytes + */ +size_t synor_crypto_falcon_public_key_size(synor_falcon_variant_t variant); + +/** + * Get the signature size for a SPHINCS+ variant. + * + * @param variant SPHINCS+ variant + * @return Signature size in bytes + */ +size_t synor_crypto_sphincs_signature_size(synor_sphincs_variant_t variant); + +/** + * Get the network name string. + * + * @param network Network type + * @return Network name ("mainnet", "testnet", or "devnet") + */ +const char* synor_crypto_network_name(synor_network_t network); + +/** + * Get human-readable error message. + * + * @param error Error code + * @return Error message string + */ +const char* synor_crypto_error_message(synor_error_t error); + +/** + * Format a derivation path as a string. + * + * @param path Derivation path + * @param buffer Output buffer + * @param buffer_size Buffer size + * @return Number of characters written (excluding null terminator) + */ +int synor_crypto_derivation_path_format( + const synor_derivation_path_t* path, + char* buffer, + size_t buffer_size +); + +#ifdef __cplusplus +} +#endif + +#endif /* SYNOR_CRYPTO_H */ diff --git a/sdk/cpp/include/synor/crypto.hpp b/sdk/cpp/include/synor/crypto.hpp new file mode 100644 index 0000000..a34d007 --- /dev/null +++ b/sdk/cpp/include/synor/crypto.hpp @@ -0,0 +1,524 @@ +/** + * Synor Crypto SDK for C++ + * + * Quantum-resistant cryptographic primitives for the Synor blockchain. + * + * Example: + * ```cpp + * #include + * + * int main() { + * synor::crypto::Config config{"your-api-key"}; + * synor::crypto::SynorCrypto crypto{config}; + * + * // Generate a mnemonic + * auto mnemonic = crypto.mnemonic().generate(24).get(); + * std::cout << "Backup words: " << mnemonic.phrase << std::endl; + * + * // Create keypair from mnemonic + * auto keypair = crypto.keypairs().from_mnemonic(mnemonic.phrase, "").get(); + * auto address = keypair.get_address(synor::crypto::Network::Mainnet); + * + * // Sign a message + * auto signature = crypto.signing().sign(keypair, "Hello!").get(); + * } + * ``` + */ + +#ifndef SYNOR_CRYPTO_HPP +#define SYNOR_CRYPTO_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace synor { +namespace crypto { + +// ============================================================================ +// Constants +// ============================================================================ + +constexpr int ED25519_PUBLIC_KEY_SIZE = 32; +constexpr int ED25519_SECRET_KEY_SIZE = 32; +constexpr int ED25519_SIGNATURE_SIZE = 64; +constexpr int DILITHIUM3_PUBLIC_KEY_SIZE = 1952; +constexpr int DILITHIUM3_SIGNATURE_SIZE = 3293; +constexpr int HYBRID_SIGNATURE_SIZE = 64 + 3293; +constexpr int COIN_TYPE = 0x5359; +constexpr int MIN_PBKDF2_ITERATIONS = 10000; +constexpr int MIN_SALT_LENGTH = 8; +constexpr const char* DEFAULT_ENDPOINT = "https://crypto.synor.io/v1"; + +// ============================================================================ +// Enumerations +// ============================================================================ + +enum class Network { + Mainnet, + Testnet, + Devnet +}; + +inline std::string to_string(Network n) { + switch (n) { + case Network::Mainnet: return "mainnet"; + case Network::Testnet: return "testnet"; + case Network::Devnet: return "devnet"; + } + return "mainnet"; +} + +inline Network network_from_string(const std::string& s) { + if (s == "testnet") return Network::Testnet; + if (s == "devnet") return Network::Devnet; + return Network::Mainnet; +} + +enum class FalconVariant { + Falcon512, + Falcon1024 +}; + +inline std::string to_string(FalconVariant v) { + switch (v) { + case FalconVariant::Falcon512: return "falcon512"; + case FalconVariant::Falcon1024: return "falcon1024"; + } + return "falcon512"; +} + +inline int signature_size(FalconVariant v) { + return v == FalconVariant::Falcon512 ? 690 : 1330; +} + +inline int public_key_size(FalconVariant v) { + return v == FalconVariant::Falcon512 ? 897 : 1793; +} + +inline int security_level(FalconVariant v) { + return v == FalconVariant::Falcon512 ? 128 : 256; +} + +enum class SphincsVariant { + Shake128s, + Shake192s, + Shake256s +}; + +inline std::string to_string(SphincsVariant v) { + switch (v) { + case SphincsVariant::Shake128s: return "shake128s"; + case SphincsVariant::Shake192s: return "shake192s"; + case SphincsVariant::Shake256s: return "shake256s"; + } + return "shake128s"; +} + +inline int signature_size(SphincsVariant v) { + switch (v) { + case SphincsVariant::Shake128s: return 7856; + case SphincsVariant::Shake192s: return 16224; + case SphincsVariant::Shake256s: return 29792; + } + return 7856; +} + +inline int security_level(SphincsVariant v) { + switch (v) { + case SphincsVariant::Shake128s: return 128; + case SphincsVariant::Shake192s: return 192; + case SphincsVariant::Shake256s: return 256; + } + return 128; +} + +enum class PqAlgorithm { + Dilithium3, + Falcon512, + Falcon1024, + Sphincs128s, + Sphincs192s, + Sphincs256s +}; + +enum class AlgorithmFamily { + Classical, + Lattice, + HashBased, + Hybrid +}; + +// ============================================================================ +// Error Types +// ============================================================================ + +class CryptoException : public std::runtime_error { +public: + explicit CryptoException(const std::string& message, const std::string& code = "") + : std::runtime_error(message), code_(code) {} + + const std::string& code() const { return code_; } + +private: + std::string code_; +}; + +// ============================================================================ +// Configuration Types +// ============================================================================ + +struct Config { + std::string api_key; + std::string endpoint = DEFAULT_ENDPOINT; + int timeout = 30000; + int retries = 3; + bool debug = false; + Network default_network = Network::Mainnet; + + explicit Config(std::string api_key) : api_key(std::move(api_key)) {} +}; + +struct DerivationConfig { + std::vector salt; + std::vector info; + int output_length = 32; +}; + +struct PasswordDerivationConfig { + std::vector salt; + int iterations = 100000; + int output_length = 32; + + explicit PasswordDerivationConfig(std::vector salt) + : salt(std::move(salt)) {} +}; + +struct DerivationPath { + static constexpr int COIN_TYPE = 0x5359; + + int account; + int change; + int index; + + static DerivationPath external_path(int account, int index) { + return {account, 0, index}; + } + + static DerivationPath internal_path(int account, int index) { + return {account, 1, index}; + } + + std::string to_string() const { + return "m/44'/" + std::to_string(COIN_TYPE) + "'/" + + std::to_string(account) + "'/" + + std::to_string(change) + "/" + + std::to_string(index); + } +}; + +// ============================================================================ +// Key Types +// ============================================================================ + +struct HybridPublicKey { + std::string ed25519; + std::string dilithium; + + std::vector ed25519_bytes() const; + std::vector dilithium_bytes() const; + size_t size() const; +}; + +struct SecretKey { + std::string ed25519_seed; + std::string master_seed; + + std::vector ed25519_seed_bytes() const; + std::vector master_seed_bytes() const; +}; + +struct FalconPublicKey { + FalconVariant variant; + std::vector bytes; +}; + +struct FalconSecretKey { + FalconVariant variant; + std::vector bytes; +}; + +struct SphincsPublicKey { + SphincsVariant variant; + std::vector bytes; +}; + +struct SphincsSecretKey { + SphincsVariant variant; + std::vector bytes; +}; + +// ============================================================================ +// Signature Types +// ============================================================================ + +struct HybridSignature { + std::string ed25519; + std::string dilithium; + + std::vector ed25519_bytes() const; + std::vector dilithium_bytes() const; + size_t size() const; + std::vector to_bytes() const; +}; + +struct FalconSignature { + FalconVariant variant; + std::vector signature; + + size_t size() const { return signature.size(); } +}; + +struct SphincsSignature { + SphincsVariant variant; + std::vector signature; + + size_t size() const { return signature.size(); } +}; + +// ============================================================================ +// Keypair Types +// ============================================================================ + +struct HybridKeypair { + HybridPublicKey public_key; + SecretKey secret_key; + std::map addresses; + + std::string get_address(Network network) const; +}; + +struct FalconKeypair { + FalconVariant variant; + FalconPublicKey public_key; + FalconSecretKey secret_key; +}; + +struct SphincsKeypair { + SphincsVariant variant; + SphincsPublicKey public_key; + SphincsSecretKey secret_key; +}; + +// ============================================================================ +// Mnemonic Types +// ============================================================================ + +struct Mnemonic { + std::string phrase; + std::vector words; + int word_count = 0; + std::vector entropy; + + std::vector get_words() const; + int get_word_count() const; +}; + +struct MnemonicValidation { + bool valid; + std::optional error; +}; + +// ============================================================================ +// Address Types +// ============================================================================ + +struct Address { + std::string address; + Network network; + std::vector pubkey_hash; +}; + +// ============================================================================ +// Hash Types +// ============================================================================ + +struct Hash256 { + std::string hash; + + std::string hex() const { return hash; } + std::vector bytes() const; +}; + +// ============================================================================ +// Forward Declarations +// ============================================================================ + +class SynorCrypto; + +// ============================================================================ +// Sub-Clients +// ============================================================================ + +class MnemonicClient { +public: + explicit MnemonicClient(SynorCrypto* crypto) : crypto_(crypto) {} + + std::future generate(int word_count = 24); + std::future from_phrase(const std::string& phrase); + std::future validate(const std::string& phrase); + std::future> to_seed(const std::string& phrase, const std::string& passphrase = ""); + std::future> suggest_words(const std::string& partial, int limit = 5); + +private: + SynorCrypto* crypto_; +}; + +class KeypairClient { +public: + explicit KeypairClient(SynorCrypto* crypto) : crypto_(crypto) {} + + std::future generate(); + std::future from_mnemonic(const std::string& phrase, const std::string& passphrase = ""); + std::future from_seed(const std::vector& seed); + std::future
get_address(const HybridPublicKey& public_key, Network network); + +private: + SynorCrypto* crypto_; +}; + +class SigningClient { +public: + explicit SigningClient(SynorCrypto* crypto) : crypto_(crypto) {} + + std::future sign(const HybridKeypair& keypair, const std::vector& message); + std::future sign(const HybridKeypair& keypair, const std::string& message); + std::future verify(const HybridPublicKey& public_key, const std::vector& message, const HybridSignature& signature); + std::future> sign_ed25519(const std::vector& secret_key, const std::vector& message); + +private: + SynorCrypto* crypto_; +}; + +class FalconClient { +public: + explicit FalconClient(SynorCrypto* crypto) : crypto_(crypto) {} + + std::future generate(FalconVariant variant = FalconVariant::Falcon512); + std::future sign(const FalconKeypair& keypair, const std::vector& message); + std::future verify(const std::vector& public_key, const std::vector& message, const FalconSignature& signature); + +private: + SynorCrypto* crypto_; +}; + +class SphincsClient { +public: + explicit SphincsClient(SynorCrypto* crypto) : crypto_(crypto) {} + + std::future generate(SphincsVariant variant = SphincsVariant::Shake128s); + std::future sign(const SphincsKeypair& keypair, const std::vector& message); + std::future verify(const std::vector& public_key, const std::vector& message, const SphincsSignature& signature); + +private: + SynorCrypto* crypto_; +}; + +class KdfClient { +public: + explicit KdfClient(SynorCrypto* crypto) : crypto_(crypto) {} + + std::future> derive_key(const std::vector& seed, const DerivationConfig& config = {}); + std::future> derive_from_password(const std::vector& password, const PasswordDerivationConfig& config); + +private: + SynorCrypto* crypto_; +}; + +class HashClient { +public: + explicit HashClient(SynorCrypto* crypto) : crypto_(crypto) {} + + std::future sha3_256(const std::vector& data); + std::future blake3(const std::vector& data); + std::future keccak256(const std::vector& data); + +private: + SynorCrypto* crypto_; +}; + +// ============================================================================ +// Main Client +// ============================================================================ + +class SynorCrypto { +public: + explicit SynorCrypto(const Config& config); + ~SynorCrypto(); + + // Non-copyable + SynorCrypto(const SynorCrypto&) = delete; + SynorCrypto& operator=(const SynorCrypto&) = delete; + + // Movable + SynorCrypto(SynorCrypto&&) noexcept; + SynorCrypto& operator=(SynorCrypto&&) noexcept; + + // Accessors + Network default_network() const { return config_.default_network; } + bool is_closed() const { return closed_; } + + // Sub-clients + MnemonicClient& mnemonic() { return mnemonic_client_; } + KeypairClient& keypairs() { return keypair_client_; } + SigningClient& signing() { return signing_client_; } + FalconClient& falcon() { return falcon_client_; } + SphincsClient& sphincs() { return sphincs_client_; } + KdfClient& kdf() { return kdf_client_; } + HashClient& hash() { return hash_client_; } + + // Operations + std::future health_check(); + void close(); + + // Internal HTTP methods (used by sub-clients) + template + std::future get(const std::string& path); + + template + std::future post(const std::string& path, const std::map& body = {}); + +private: + void check_closed() const; + + Config config_; + bool closed_ = false; + + MnemonicClient mnemonic_client_; + KeypairClient keypair_client_; + SigningClient signing_client_; + FalconClient falcon_client_; + SphincsClient sphincs_client_; + KdfClient kdf_client_; + HashClient hash_client_; + + class Impl; + std::unique_ptr impl_; +}; + +// ============================================================================ +// Utility Functions +// ============================================================================ + +std::string base64_encode(const std::vector& data); +std::vector base64_decode(const std::string& encoded); + +} // namespace crypto +} // namespace synor + +#endif // SYNOR_CRYPTO_HPP diff --git a/sdk/csharp/src/Synor.Crypto/SynorCrypto.cs b/sdk/csharp/src/Synor.Crypto/SynorCrypto.cs new file mode 100644 index 0000000..e9b5831 --- /dev/null +++ b/sdk/csharp/src/Synor.Crypto/SynorCrypto.cs @@ -0,0 +1,336 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Json; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; + +namespace Synor.Crypto; + +/// +/// Synor Crypto SDK for C#/.NET +/// +/// Quantum-resistant cryptographic primitives for the Synor blockchain. +/// +public class SynorCrypto : IDisposable +{ + private readonly CryptoConfig _config; + private readonly HttpClient _httpClient; + private readonly JsonSerializerOptions _jsonOptions; + private bool _closed; + + public MnemonicClient Mnemonic { get; } + public KeypairClient Keypairs { get; } + public SigningClient Signing { get; } + public FalconClient Falcon { get; } + public SphincsClient Sphincs { get; } + public KdfClient Kdf { get; } + public HashClient Hash { get; } + + public SynorCrypto(CryptoConfig config) + { + _config = config; + _httpClient = new HttpClient + { + BaseAddress = new Uri(config.Endpoint), + Timeout = TimeSpan.FromMilliseconds(config.Timeout) + }; + _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {config.ApiKey}"); + _httpClient.DefaultRequestHeaders.Add("X-SDK-Version", "csharp/0.1.0"); + + _jsonOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, + Converters = { new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseLower) } + }; + + Mnemonic = new MnemonicClient(this); + Keypairs = new KeypairClient(this); + Signing = new SigningClient(this); + Falcon = new FalconClient(this); + Sphincs = new SphincsClient(this); + Kdf = new KdfClient(this); + Hash = new HashClient(this); + } + + public Network DefaultNetwork => _config.DefaultNetwork; + + public async Task HealthCheckAsync(CancellationToken ct = default) + { + try + { + var result = await GetAsync>("/health", ct); + return result.TryGetValue("status", out var status) && status?.ToString() == "healthy"; + } + catch + { + return false; + } + } + + public Task> GetInfoAsync(CancellationToken ct = default) => + GetAsync>("/info", ct); + + public void Close() + { + _closed = true; + _httpClient.Dispose(); + } + + public bool IsClosed => _closed; + + public void Dispose() + { + Close(); + GC.SuppressFinalize(this); + } + + internal async Task GetAsync(string path, CancellationToken ct = default) + { + CheckClosed(); + var response = await _httpClient.GetAsync(path, ct); + return await HandleResponseAsync(response); + } + + internal async Task PostAsync(string path, object? body = null, CancellationToken ct = default) + { + CheckClosed(); + var content = body != null + ? JsonContent.Create(body, options: _jsonOptions) + : JsonContent.Create(new { }); + var response = await _httpClient.PostAsync(path, content, ct); + return await HandleResponseAsync(response); + } + + private async Task HandleResponseAsync(HttpResponseMessage response) + { + var json = await response.Content.ReadAsStringAsync(); + if (!response.IsSuccessStatusCode) + { + var error = JsonSerializer.Deserialize>(json, _jsonOptions) ?? new(); + throw new CryptoException( + error.TryGetValue("message", out var msg) ? msg?.ToString() ?? $"HTTP {(int)response.StatusCode}" : $"HTTP {(int)response.StatusCode}", + error.TryGetValue("code", out var code) ? code?.ToString() : null, + (int)response.StatusCode + ); + } + return JsonSerializer.Deserialize(json, _jsonOptions)!; + } + + private void CheckClosed() + { + if (_closed) throw new CryptoException("Client has been closed", "CLIENT_CLOSED"); + } +} + +/// Mnemonic sub-client +public class MnemonicClient +{ + private readonly SynorCrypto _crypto; + + internal MnemonicClient(SynorCrypto crypto) => _crypto = crypto; + + public Task GenerateAsync(int wordCount = 24, CancellationToken ct = default) => + _crypto.PostAsync("/mnemonic/generate", new { word_count = wordCount }, ct); + + public Task FromPhraseAsync(string phrase, CancellationToken ct = default) => + _crypto.PostAsync("/mnemonic/from-phrase", new { phrase }, ct); + + public Task ValidateAsync(string phrase, CancellationToken ct = default) => + _crypto.PostAsync("/mnemonic/validate", new { phrase }, ct); + + public async Task ToSeedAsync(string phrase, string passphrase = "", CancellationToken ct = default) + { + var result = await _crypto.PostAsync("/mnemonic/to-seed", new { phrase, passphrase }, ct); + return Convert.FromBase64String(result.Seed); + } + + public async Task> SuggestWordsAsync(string partial, int limit = 5, CancellationToken ct = default) + { + var result = await _crypto.PostAsync("/mnemonic/suggest", new { partial, limit }, ct); + return result.Suggestions ?? new List(); + } +} + +/// Keypair sub-client +public class KeypairClient +{ + private readonly SynorCrypto _crypto; + + internal KeypairClient(SynorCrypto crypto) => _crypto = crypto; + + public Task GenerateAsync(CancellationToken ct = default) => + _crypto.PostAsync("/keypair/generate", null, ct); + + public Task FromMnemonicAsync(string phrase, string passphrase = "", CancellationToken ct = default) => + _crypto.PostAsync("/keypair/from-mnemonic", new { phrase, passphrase }, ct); + + public Task FromSeedAsync(byte[] seed, CancellationToken ct = default) => + _crypto.PostAsync("/keypair/from-seed", new { seed = Convert.ToBase64String(seed) }, ct); + + public Task
GetAddressAsync(HybridPublicKey publicKey, Network network, CancellationToken ct = default) => + _crypto.PostAsync
("/keypair/address", new + { + public_key = publicKey.ToDict(), + network = network.ToApiString() + }, ct); +} + +/// Signing sub-client +public class SigningClient +{ + private readonly SynorCrypto _crypto; + + internal SigningClient(SynorCrypto crypto) => _crypto = crypto; + + public Task SignAsync(HybridKeypair keypair, byte[] message, CancellationToken ct = default) => + _crypto.PostAsync("/sign/hybrid", new + { + secret_key = keypair.SecretKey.ToDict(), + message = Convert.ToBase64String(message) + }, ct); + + public async Task VerifyAsync(HybridPublicKey publicKey, byte[] message, HybridSignature signature, CancellationToken ct = default) + { + var result = await _crypto.PostAsync("/sign/verify", new + { + public_key = publicKey.ToDict(), + message = Convert.ToBase64String(message), + signature = signature.ToDict() + }, ct); + return result.Valid; + } + + public async Task SignEd25519Async(byte[] secretKey, byte[] message, CancellationToken ct = default) + { + var result = await _crypto.PostAsync("/sign/ed25519", new + { + secret_key = Convert.ToBase64String(secretKey), + message = Convert.ToBase64String(message) + }, ct); + return Convert.FromBase64String(result.Signature); + } +} + +/// Falcon sub-client +public class FalconClient +{ + private readonly SynorCrypto _crypto; + + internal FalconClient(SynorCrypto crypto) => _crypto = crypto; + + public Task GenerateAsync(FalconVariant variant = FalconVariant.Falcon512, CancellationToken ct = default) => + _crypto.PostAsync("/falcon/generate", new { variant = variant.ToApiString() }, ct); + + public Task SignAsync(FalconKeypair keypair, byte[] message, CancellationToken ct = default) => + _crypto.PostAsync("/falcon/sign", new + { + variant = keypair.VariantEnum.ToApiString(), + secret_key = Convert.ToBase64String(keypair.SecretKey.KeyBytes), + message = Convert.ToBase64String(message) + }, ct); + + public async Task VerifyAsync(byte[] publicKey, byte[] message, FalconSignature signature, CancellationToken ct = default) + { + var result = await _crypto.PostAsync("/falcon/verify", new + { + variant = signature.VariantEnum.ToApiString(), + public_key = Convert.ToBase64String(publicKey), + message = Convert.ToBase64String(message), + signature = Convert.ToBase64String(signature.SignatureBytes) + }, ct); + return result.Valid; + } +} + +/// SPHINCS+ sub-client +public class SphincsClient +{ + private readonly SynorCrypto _crypto; + + internal SphincsClient(SynorCrypto crypto) => _crypto = crypto; + + public Task GenerateAsync(SphincsVariant variant = SphincsVariant.Shake128s, CancellationToken ct = default) => + _crypto.PostAsync("/sphincs/generate", new { variant = variant.ToApiString() }, ct); + + public Task SignAsync(SphincsKeypair keypair, byte[] message, CancellationToken ct = default) => + _crypto.PostAsync("/sphincs/sign", new + { + variant = keypair.VariantEnum.ToApiString(), + secret_key = Convert.ToBase64String(keypair.SecretKey.KeyBytes), + message = Convert.ToBase64String(message) + }, ct); + + public async Task VerifyAsync(byte[] publicKey, byte[] message, SphincsSignature signature, CancellationToken ct = default) + { + var result = await _crypto.PostAsync("/sphincs/verify", new + { + variant = signature.VariantEnum.ToApiString(), + public_key = Convert.ToBase64String(publicKey), + message = Convert.ToBase64String(message), + signature = Convert.ToBase64String(signature.SignatureBytes) + }, ct); + return result.Valid; + } +} + +/// KDF sub-client +public class KdfClient +{ + private readonly SynorCrypto _crypto; + + internal KdfClient(SynorCrypto crypto) => _crypto = crypto; + + public async Task DeriveKeyAsync(byte[] seed, DerivationConfig? config = null, CancellationToken ct = default) + { + config ??= new DerivationConfig(); + var body = new Dictionary + { + ["seed"] = Convert.ToBase64String(seed), + ["output_length"] = config.OutputLength + }; + if (config.Salt != null) body["salt"] = Convert.ToBase64String(config.Salt); + if (config.Info != null) body["info"] = Convert.ToBase64String(config.Info); + + var result = await _crypto.PostAsync("/kdf/hkdf", body, ct); + return Convert.FromBase64String(result.Key); + } + + public async Task DeriveFromPasswordAsync(byte[] password, PasswordDerivationConfig config, CancellationToken ct = default) + { + var result = await _crypto.PostAsync("/kdf/pbkdf2", new + { + password = Convert.ToBase64String(password), + salt = Convert.ToBase64String(config.Salt), + iterations = config.Iterations, + output_length = config.OutputLength + }, ct); + return Convert.FromBase64String(result.Key); + } +} + +/// Hash sub-client +public class HashClient +{ + private readonly SynorCrypto _crypto; + + internal HashClient(SynorCrypto crypto) => _crypto = crypto; + + public Task Sha3_256Async(byte[] data, CancellationToken ct = default) => + _crypto.PostAsync("/hash/sha3-256", new { data = Convert.ToBase64String(data) }, ct); + + public Task Blake3Async(byte[] data, CancellationToken ct = default) => + _crypto.PostAsync("/hash/blake3", new { data = Convert.ToBase64String(data) }, ct); + + public Task Keccak256Async(byte[] data, CancellationToken ct = default) => + _crypto.PostAsync("/hash/keccak256", new { data = Convert.ToBase64String(data) }, ct); +} + +// Response helper types +internal record SeedResponse(string Seed); +internal record SuggestResponse(List? Suggestions); +internal record SignatureResponse(string Signature); +internal record KeyResponse(string Key); +internal record ValidResponse(bool Valid); diff --git a/sdk/csharp/src/Synor.Crypto/Types.cs b/sdk/csharp/src/Synor.Crypto/Types.cs new file mode 100644 index 0000000..2a2f789 --- /dev/null +++ b/sdk/csharp/src/Synor.Crypto/Types.cs @@ -0,0 +1,505 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Synor.Crypto; + +// ============================================================================ +// Enumerations +// ============================================================================ + +/// Network type for the Synor blockchain. +public enum Network +{ + Mainnet, + Testnet, + Devnet +} + +public static class NetworkExtensions +{ + public static string ToApiString(this Network network) => network switch + { + Network.Mainnet => "mainnet", + Network.Testnet => "testnet", + Network.Devnet => "devnet", + _ => "mainnet" + }; + + public static Network FromApiString(string value) => value switch + { + "mainnet" => Network.Mainnet, + "testnet" => Network.Testnet, + "devnet" => Network.Devnet, + _ => Network.Mainnet + }; +} + +/// Falcon signature variant. +public enum FalconVariant +{ + Falcon512, + Falcon1024 +} + +public static class FalconVariantExtensions +{ + public static string ToApiString(this FalconVariant variant) => variant switch + { + FalconVariant.Falcon512 => "falcon512", + FalconVariant.Falcon1024 => "falcon1024", + _ => "falcon512" + }; + + public static int SignatureSize(this FalconVariant variant) => variant switch + { + FalconVariant.Falcon512 => 690, + FalconVariant.Falcon1024 => 1330, + _ => 690 + }; + + public static int PublicKeySize(this FalconVariant variant) => variant switch + { + FalconVariant.Falcon512 => 897, + FalconVariant.Falcon1024 => 1793, + _ => 897 + }; + + public static int SecurityLevel(this FalconVariant variant) => variant switch + { + FalconVariant.Falcon512 => 128, + FalconVariant.Falcon1024 => 256, + _ => 128 + }; +} + +/// SPHINCS+ signature variant. +public enum SphincsVariant +{ + Shake128s, + Shake192s, + Shake256s +} + +public static class SphincsVariantExtensions +{ + public static string ToApiString(this SphincsVariant variant) => variant switch + { + SphincsVariant.Shake128s => "shake128s", + SphincsVariant.Shake192s => "shake192s", + SphincsVariant.Shake256s => "shake256s", + _ => "shake128s" + }; + + public static int SignatureSize(this SphincsVariant variant) => variant switch + { + SphincsVariant.Shake128s => 7856, + SphincsVariant.Shake192s => 16224, + SphincsVariant.Shake256s => 29792, + _ => 7856 + }; + + public static int SecurityLevel(this SphincsVariant variant) => variant switch + { + SphincsVariant.Shake128s => 128, + SphincsVariant.Shake192s => 192, + SphincsVariant.Shake256s => 256, + _ => 128 + }; +} + +/// Post-quantum algorithm types. +public enum PqAlgorithm +{ + Dilithium3, + Falcon512, + Falcon1024, + Sphincs128s, + Sphincs192s, + Sphincs256s +} + +/// Cryptographic algorithm family. +public enum AlgorithmFamily +{ + Classical, + Lattice, + HashBased, + Hybrid +} + +// ============================================================================ +// Configuration Types +// ============================================================================ + +/// Configuration for the Crypto SDK. +public record CryptoConfig +{ + public required string ApiKey { get; init; } + public string Endpoint { get; init; } = "https://crypto.synor.io/v1"; + public int Timeout { get; init; } = 30000; + public int Retries { get; init; } = 3; + public bool Debug { get; init; } = false; + public Network DefaultNetwork { get; init; } = Network.Mainnet; +} + +/// Configuration for key derivation. +public record DerivationConfig +{ + public byte[]? Salt { get; init; } + public byte[]? Info { get; init; } + public int OutputLength { get; init; } = 32; +} + +/// Configuration for password-based key derivation. +public record PasswordDerivationConfig +{ + public required byte[] Salt { get; init; } + public int Iterations { get; init; } = 100000; + public int OutputLength { get; init; } = 32; +} + +/// BIP-44 derivation path. +public record DerivationPath(int Account, int Change, int Index) +{ + public const int CoinType = 0x5359; + + public static DerivationPath External(int account, int index) => new(account, 0, index); + public static DerivationPath Internal(int account, int index) => new(account, 1, index); + + public override string ToString() => $"m/44'/{CoinType}'/{Account}'/{Change}/{Index}"; + + public Dictionary ToDict() => new() + { + ["account"] = Account, + ["change"] = Change, + ["index"] = Index + }; +} + +// ============================================================================ +// Key Types +// ============================================================================ + +/// Hybrid public key combining Ed25519 and Dilithium3. +public record HybridPublicKey +{ + public string Ed25519 { get; init; } = ""; + public string Dilithium { get; init; } = ""; + + public byte[] Ed25519Bytes => Convert.FromBase64String(Ed25519); + public byte[] DilithiumBytes => Convert.FromBase64String(Dilithium); + public int Size => Ed25519Bytes.Length + DilithiumBytes.Length; + + public Dictionary ToDict() => new() + { + ["ed25519"] = Ed25519, + ["dilithium"] = Dilithium + }; +} + +/// Secret key for hybrid signatures. +public record SecretKey +{ + [JsonPropertyName("ed25519_seed")] + public string Ed25519Seed { get; init; } = ""; + + [JsonPropertyName("master_seed")] + public string MasterSeed { get; init; } = ""; + + public byte[] Ed25519SeedBytes => Convert.FromBase64String(Ed25519Seed); + public byte[] MasterSeedBytes => Convert.FromBase64String(MasterSeed); + + public Dictionary ToDict() => new() + { + ["ed25519_seed"] = Ed25519Seed, + ["master_seed"] = MasterSeed + }; +} + +/// Falcon public key. +public record FalconPublicKey +{ + public string Variant { get; init; } = ""; + public string Bytes { get; init; } = ""; + + public FalconVariant VariantEnum => Variant.ToLower() switch + { + "falcon512" => FalconVariant.Falcon512, + "falcon1024" => FalconVariant.Falcon1024, + _ => FalconVariant.Falcon512 + }; + + public byte[] KeyBytes => Convert.FromBase64String(Bytes); +} + +/// Falcon secret key. +public record FalconSecretKey +{ + public string Variant { get; init; } = ""; + public string Bytes { get; init; } = ""; + + public FalconVariant VariantEnum => Variant.ToLower() switch + { + "falcon512" => FalconVariant.Falcon512, + "falcon1024" => FalconVariant.Falcon1024, + _ => FalconVariant.Falcon512 + }; + + public byte[] KeyBytes => Convert.FromBase64String(Bytes); +} + +/// SPHINCS+ public key. +public record SphincsPublicKey +{ + public string Variant { get; init; } = ""; + public string Bytes { get; init; } = ""; + + public SphincsVariant VariantEnum => Variant.ToLower() switch + { + "shake128s" => SphincsVariant.Shake128s, + "shake192s" => SphincsVariant.Shake192s, + "shake256s" => SphincsVariant.Shake256s, + _ => SphincsVariant.Shake128s + }; + + public byte[] KeyBytes => Convert.FromBase64String(Bytes); +} + +/// SPHINCS+ secret key. +public record SphincsSecretKey +{ + public string Variant { get; init; } = ""; + public string Bytes { get; init; } = ""; + + public SphincsVariant VariantEnum => Variant.ToLower() switch + { + "shake128s" => SphincsVariant.Shake128s, + "shake192s" => SphincsVariant.Shake192s, + "shake256s" => SphincsVariant.Shake256s, + _ => SphincsVariant.Shake128s + }; + + public byte[] KeyBytes => Convert.FromBase64String(Bytes); +} + +// ============================================================================ +// Signature Types +// ============================================================================ + +/// Hybrid signature combining Ed25519 and Dilithium3. +public record HybridSignature +{ + public string Ed25519 { get; init; } = ""; + public string Dilithium { get; init; } = ""; + + public byte[] Ed25519Bytes => Convert.FromBase64String(Ed25519); + public byte[] DilithiumBytes => Convert.FromBase64String(Dilithium); + public int Size => Ed25519Bytes.Length + DilithiumBytes.Length; + + public byte[] ToBytes() + { + var e = Ed25519Bytes; + var d = DilithiumBytes; + var result = new byte[e.Length + d.Length]; + Buffer.BlockCopy(e, 0, result, 0, e.Length); + Buffer.BlockCopy(d, 0, result, e.Length, d.Length); + return result; + } + + public Dictionary ToDict() => new() + { + ["ed25519"] = Ed25519, + ["dilithium"] = Dilithium + }; +} + +/// Falcon signature. +public record FalconSignature +{ + public string Variant { get; init; } = ""; + public string Signature { get; init; } = ""; + + public FalconVariant VariantEnum => Variant.ToLower() switch + { + "falcon512" => FalconVariant.Falcon512, + "falcon1024" => FalconVariant.Falcon1024, + _ => FalconVariant.Falcon512 + }; + + public byte[] SignatureBytes => Convert.FromBase64String(Signature); + public int Size => SignatureBytes.Length; +} + +/// SPHINCS+ signature. +public record SphincsSignature +{ + public string Variant { get; init; } = ""; + public string Signature { get; init; } = ""; + + public SphincsVariant VariantEnum => Variant.ToLower() switch + { + "shake128s" => SphincsVariant.Shake128s, + "shake192s" => SphincsVariant.Shake192s, + "shake256s" => SphincsVariant.Shake256s, + _ => SphincsVariant.Shake128s + }; + + public byte[] SignatureBytes => Convert.FromBase64String(Signature); + public int Size => SignatureBytes.Length; +} + +// ============================================================================ +// Keypair Types +// ============================================================================ + +/// Hybrid keypair for Ed25519 + Dilithium3. +public record HybridKeypair +{ + [JsonPropertyName("public_key")] + public HybridPublicKey PublicKey { get; init; } = new(); + + [JsonPropertyName("secret_key")] + public SecretKey SecretKey { get; init; } = new(); + + public Dictionary Addresses { get; init; } = new(); + + public string GetAddress(Network network) => + Addresses.TryGetValue(network.ToApiString(), out var addr) ? addr : ""; +} + +/// Falcon keypair. +public record FalconKeypair +{ + public string Variant { get; init; } = ""; + + [JsonPropertyName("public_key")] + public FalconPublicKey PublicKey { get; init; } = new(); + + [JsonPropertyName("secret_key")] + public FalconSecretKey SecretKey { get; init; } = new(); + + public FalconVariant VariantEnum => PublicKey.VariantEnum; +} + +/// SPHINCS+ keypair. +public record SphincsKeypair +{ + public string Variant { get; init; } = ""; + + [JsonPropertyName("public_key")] + public SphincsPublicKey PublicKey { get; init; } = new(); + + [JsonPropertyName("secret_key")] + public SphincsSecretKey SecretKey { get; init; } = new(); + + public SphincsVariant VariantEnum => PublicKey.VariantEnum; +} + +// ============================================================================ +// Mnemonic Types +// ============================================================================ + +/// BIP-39 mnemonic phrase. +public record Mnemonic +{ + public string Phrase { get; init; } = ""; + public List? Words { get; init; } + + [JsonPropertyName("word_count")] + public int WordCount { get; init; } + + public string? Entropy { get; init; } + + public List GetWords() => Words ?? new List(Phrase.Split(' ')); + public int GetWordCount() => WordCount > 0 ? WordCount : GetWords().Count; + public byte[] GetEntropy() => Entropy != null ? Convert.FromBase64String(Entropy) : Array.Empty(); +} + +/// Mnemonic validation result. +public record MnemonicValidation +{ + public bool Valid { get; init; } + public string? Error { get; init; } +} + +// ============================================================================ +// Address Types +// ============================================================================ + +/// Blockchain address. +public record Address +{ + [JsonPropertyName("address")] + public string AddressString { get; init; } = ""; + + public string NetworkString { get; init; } = ""; + + [JsonPropertyName("pubkey_hash")] + public string? PubkeyHash { get; init; } + + public Network Network => NetworkExtensions.FromApiString(NetworkString); + public byte[] PubkeyHashBytes => PubkeyHash != null ? Convert.FromBase64String(PubkeyHash) : Array.Empty(); +} + +// ============================================================================ +// Hash Types +// ============================================================================ + +/// 256-bit hash result. +public record Hash256 +{ + public string Hash { get; init; } = ""; + + public string Hex => Hash; + + public byte[] Bytes + { + get + { + var result = new byte[Hash.Length / 2]; + for (int i = 0; i < result.Length; i++) + { + result[i] = Convert.ToByte(Hash.Substring(i * 2, 2), 16); + } + return result; + } + } +} + +// ============================================================================ +// Error Types +// ============================================================================ + +/// Crypto SDK exception. +public class CryptoException : Exception +{ + public string? Code { get; } + public int? HttpStatus { get; } + + public CryptoException(string message, string? code = null, int? httpStatus = null) + : base(message) + { + Code = code; + HttpStatus = httpStatus; + } +} + +// ============================================================================ +// Constants +// ============================================================================ + +/// Cryptographic constants. +public static class CryptoConstants +{ + public const int Ed25519PublicKeySize = 32; + public const int Ed25519SecretKeySize = 32; + public const int Ed25519SignatureSize = 64; + public const int Dilithium3PublicKeySize = 1952; + public const int Dilithium3SignatureSize = 3293; + public const int HybridSignatureSize = 64 + 3293; + public const int CoinType = 0x5359; + public const int MinPbkdf2Iterations = 10000; + public const int MinSaltLength = 8; + public const string DefaultEndpoint = "https://crypto.synor.io/v1"; +} diff --git a/sdk/flutter/lib/src/crypto/client.dart b/sdk/flutter/lib/src/crypto/client.dart new file mode 100644 index 0000000..d398fd7 --- /dev/null +++ b/sdk/flutter/lib/src/crypto/client.dart @@ -0,0 +1,466 @@ +/// Synor Crypto SDK Client for Flutter/Dart +/// +/// Quantum-resistant cryptographic primitives for the Synor blockchain. +library synor_crypto; + +import 'dart:convert'; +import 'dart:typed_data'; +import 'package:http/http.dart' as http; +import 'types.dart'; + +export 'types.dart'; + +/// Main Synor Crypto client +class SynorCrypto { + final CryptoConfig _config; + final http.Client _client; + bool _closed = false; + + late final MnemonicClient mnemonic; + late final KeypairClient keypairs; + late final SigningClient signing; + late final FalconClient falcon; + late final SphincsClient sphincs; + late final KdfClient kdf; + late final HashClient hash; + late final NegotiationClient negotiation; + + SynorCrypto(this._config) : _client = http.Client() { + mnemonic = MnemonicClient(this); + keypairs = KeypairClient(this); + signing = SigningClient(this); + falcon = FalconClient(this); + sphincs = SphincsClient(this); + kdf = KdfClient(this); + hash = HashClient(this); + negotiation = NegotiationClient(this); + } + + /// Returns the default network + Network get defaultNetwork => _config.defaultNetwork; + + /// Checks service health + Future healthCheck() async { + try { + final result = await _get('/health'); + return result['status'] == 'healthy'; + } catch (_) { + return false; + } + } + + /// Gets service info + Future> getInfo() => _get('/info'); + + /// Closes the client + void close() { + _closed = true; + _client.close(); + } + + /// Returns true if closed + bool get isClosed => _closed; + + Future> _get(String path) async { + _checkClosed(); + final response = await _client.get( + Uri.parse('${_config.endpoint}$path'), + headers: _headers(), + ); + return _handleResponse(response); + } + + Future> _post(String path, [Map? body]) async { + _checkClosed(); + final response = await _client.post( + Uri.parse('${_config.endpoint}$path'), + headers: _headers(), + body: body != null ? jsonEncode(body) : null, + ); + return _handleResponse(response); + } + + Map _headers() => { + 'Authorization': 'Bearer ${_config.apiKey}', + 'Content-Type': 'application/json', + 'X-SDK-Version': 'dart/0.1.0', + }; + + Map _handleResponse(http.Response response) { + final json = jsonDecode(response.body) as Map; + if (response.statusCode >= 400) { + throw CryptoException( + json['message'] as String? ?? 'HTTP ${response.statusCode}', + json['code'] as String?, + ); + } + return json; + } + + void _checkClosed() { + if (_closed) { + throw const CryptoException('Client has been closed', 'CLIENT_CLOSED'); + } + } +} + +/// Mnemonic operations +class MnemonicClient { + final SynorCrypto _crypto; + MnemonicClient(this._crypto); + + /// Generates a new random mnemonic + Future generate([int wordCount = 24]) async { + final result = await _crypto._post('/mnemonic/generate', {'word_count': wordCount}); + return Mnemonic.fromJson(result); + } + + /// Creates a mnemonic from a phrase + Future fromPhrase(String phrase) async { + final result = await _crypto._post('/mnemonic/from-phrase', {'phrase': phrase}); + return Mnemonic.fromJson(result); + } + + /// Creates a mnemonic from entropy + Future fromEntropy(Uint8List entropy) async { + final result = await _crypto._post('/mnemonic/from-entropy', { + 'entropy': base64Encode(entropy), + }); + return Mnemonic.fromJson(result); + } + + /// Validates a mnemonic phrase + Future validate(String phrase) async { + final result = await _crypto._post('/mnemonic/validate', {'phrase': phrase}); + return MnemonicValidation.fromJson(result); + } + + /// Derives a seed from a mnemonic + Future toSeed(String phrase, [String passphrase = '']) async { + final result = await _crypto._post('/mnemonic/to-seed', { + 'phrase': phrase, + 'passphrase': passphrase, + }); + return base64Decode(result['seed'] as String); + } + + /// Suggests word completions + Future> suggestWords(String partial, [int limit = 10]) async { + final result = await _crypto._post('/mnemonic/suggest', { + 'partial': partial, + 'limit': limit, + }); + return List.from(result['suggestions'] as List); + } +} + +/// Keypair operations +class KeypairClient { + final SynorCrypto _crypto; + KeypairClient(this._crypto); + + /// Generates a new random keypair + Future generate() async { + final result = await _crypto._post('/keypair/generate'); + return HybridKeypair.fromJson(result); + } + + /// Creates a keypair from a mnemonic + Future fromMnemonic(String phrase, [String passphrase = '']) async { + final result = await _crypto._post('/keypair/from-mnemonic', { + 'phrase': phrase, + 'passphrase': passphrase, + }); + return HybridKeypair.fromJson(result); + } + + /// Creates a keypair from a seed + Future fromSeed(Uint8List seed) async { + final result = await _crypto._post('/keypair/from-seed', { + 'seed': base64Encode(seed), + }); + return HybridKeypair.fromJson(result); + } + + /// Derives a child keypair + Future derive(HybridKeypair parent, DerivationPath path) async { + final result = await _crypto._post('/keypair/derive', { + 'public_key': parent.publicKey.toJson(), + 'path': path.toJson(), + }); + return HybridKeypair.fromJson(result); + } + + /// Gets the address for a public key + Future
getAddress(HybridPublicKey publicKey, Network network) async { + final result = await _crypto._post('/keypair/address', { + 'public_key': publicKey.toJson(), + 'network': network.toJson(), + }); + return Address.fromJson(result); + } +} + +/// Signing operations +class SigningClient { + final SynorCrypto _crypto; + SigningClient(this._crypto); + + /// Signs a message with a hybrid keypair + Future sign(HybridKeypair keypair, Uint8List message) async { + final result = await _crypto._post('/sign/hybrid', { + 'secret_key': { + 'ed25519_seed': base64Encode(keypair.secretKey.ed25519Seed), + 'master_seed': base64Encode(keypair.secretKey.masterSeed), + }, + 'message': base64Encode(message), + }); + return HybridSignature.fromJson(result); + } + + /// Verifies a hybrid signature + Future verify( + HybridPublicKey publicKey, + Uint8List message, + HybridSignature signature, + ) async { + final result = await _crypto._post('/sign/verify', { + 'public_key': publicKey.toJson(), + 'message': base64Encode(message), + 'signature': signature.toJson(), + }); + return result['valid'] as bool; + } + + /// Signs with Ed25519 only + Future signEd25519(Uint8List secretKey, Uint8List message) async { + final result = await _crypto._post('/sign/ed25519', { + 'secret_key': base64Encode(secretKey), + 'message': base64Encode(message), + }); + return base64Decode(result['signature'] as String); + } + + /// Verifies an Ed25519 signature + Future verifyEd25519( + Uint8List publicKey, + Uint8List message, + Uint8List signature, + ) async { + final result = await _crypto._post('/sign/verify-ed25519', { + 'public_key': base64Encode(publicKey), + 'message': base64Encode(message), + 'signature': base64Encode(signature), + }); + return result['valid'] as bool; + } +} + +/// Falcon operations +class FalconClient { + final SynorCrypto _crypto; + FalconClient(this._crypto); + + /// Generates a Falcon keypair + Future generate([FalconVariant variant = FalconVariant.falcon512]) async { + final result = await _crypto._post('/falcon/generate', {'variant': variant.toJson()}); + return FalconKeypair.fromJson(result); + } + + /// Signs with Falcon + Future sign(FalconKeypair keypair, Uint8List message) async { + final result = await _crypto._post('/falcon/sign', { + 'variant': keypair.variant.toJson(), + 'secret_key': base64Encode(keypair.secretKey.bytes), + 'message': base64Encode(message), + }); + return FalconSignature.fromJson(result); + } + + /// Verifies a Falcon signature + Future verify( + Uint8List publicKey, + Uint8List message, + FalconSignature signature, + ) async { + final result = await _crypto._post('/falcon/verify', { + 'variant': signature.variant.toJson(), + 'public_key': base64Encode(publicKey), + 'message': base64Encode(message), + 'signature': base64Encode(signature.bytes), + }); + return result['valid'] as bool; + } +} + +/// SPHINCS+ operations +class SphincsClient { + final SynorCrypto _crypto; + SphincsClient(this._crypto); + + /// Generates a SPHINCS+ keypair + Future generate([SphincsVariant variant = SphincsVariant.shake128s]) async { + final result = await _crypto._post('/sphincs/generate', {'variant': variant.toJson()}); + return SphincsKeypair.fromJson(result); + } + + /// Signs with SPHINCS+ + Future sign(SphincsKeypair keypair, Uint8List message) async { + final result = await _crypto._post('/sphincs/sign', { + 'variant': keypair.variant.toJson(), + 'secret_key': base64Encode(keypair.secretKey.bytes), + 'message': base64Encode(message), + }); + return SphincsSignature.fromJson(result); + } + + /// Verifies a SPHINCS+ signature + Future verify( + Uint8List publicKey, + Uint8List message, + SphincsSignature signature, + ) async { + final result = await _crypto._post('/sphincs/verify', { + 'variant': signature.variant.toJson(), + 'public_key': base64Encode(publicKey), + 'message': base64Encode(message), + 'signature': base64Encode(signature.bytes), + }); + return result['valid'] as bool; + } +} + +/// Key derivation operations +class KdfClient { + final SynorCrypto _crypto; + KdfClient(this._crypto); + + /// Derives a key using HKDF + Future deriveKey(Uint8List seed, [DerivationConfig? config]) async { + config ??= const DerivationConfig(); + final body = { + 'seed': base64Encode(seed), + 'output_length': config.outputLength, + }; + if (config.salt != null) body['salt'] = base64Encode(config.salt!); + if (config.info != null) body['info'] = base64Encode(config.info!); + + final result = await _crypto._post('/kdf/hkdf', body); + return base64Decode(result['key'] as String); + } + + /// Derives a key from a password + Future deriveFromPassword( + String password, + PasswordDerivationConfig config, + ) async { + final result = await _crypto._post('/kdf/pbkdf2', { + 'password': base64Encode(utf8.encode(password)), + 'salt': base64Encode(config.salt), + 'iterations': config.iterations, + 'output_length': config.outputLength, + }); + return base64Decode(result['key'] as String); + } + + /// Derives a child key + Future<({Uint8List key, Uint8List chainCode})> deriveChildKey( + Uint8List parentKey, + Uint8List chainCode, + int index, + ) async { + final result = await _crypto._post('/kdf/child', { + 'parent_key': base64Encode(parentKey), + 'chain_code': base64Encode(chainCode), + 'index': index, + }); + return ( + key: base64Decode(result['key'] as String), + chainCode: base64Decode(result['chain_code'] as String), + ); + } +} + +/// Hashing operations +class HashClient { + final SynorCrypto _crypto; + HashClient(this._crypto); + + /// Computes SHA3-256 hash + Future sha3_256(Uint8List data) async { + final result = await _crypto._post('/hash/sha3-256', { + 'data': base64Encode(data), + }); + return Hash256.fromJson(result); + } + + /// Computes BLAKE3 hash + Future blake3(Uint8List data) async { + final result = await _crypto._post('/hash/blake3', { + 'data': base64Encode(data), + }); + return Hash256.fromJson(result); + } + + /// Computes Keccak-256 hash + Future keccak256(Uint8List data) async { + final result = await _crypto._post('/hash/keccak256', { + 'data': base64Encode(data), + }); + return Hash256.fromJson(result); + } + + /// Combines multiple hashes + Future combine(List hashes) async { + final result = await _crypto._post('/hash/combine', { + 'hashes': hashes.map(base64Encode).toList(), + }); + return Hash256.fromJson(result); + } +} + +/// Algorithm negotiation operations +class NegotiationClient { + final SynorCrypto _crypto; + NegotiationClient(this._crypto); + + /// Gets local capabilities + Future getCapabilities() async { + final result = await _crypto._get('/negotiation/capabilities'); + return AlgorithmCapabilities.fromJson(result); + } + + /// Negotiates with a peer + Future negotiate( + AlgorithmCapabilities peerCapabilities, + NegotiationPolicy policy, + ) async { + final result = await _crypto._post('/negotiation/negotiate', { + 'peer_capabilities': { + 'pq_algorithms': peerCapabilities.pqAlgorithms.map((e) => e.toJson()).toList(), + 'classical': peerCapabilities.classical, + 'hybrid': peerCapabilities.hybrid, + 'preferred': peerCapabilities.preferred?.toJson(), + }, + 'policy': policy.toJson(), + }); + return NegotiationResult.fromJson(result); + } + + /// Establishes session parameters + Future establishSession( + NegotiationResult result, + Uint8List peerPublicKey, + ) async { + final resp = await _crypto._post('/negotiation/session', { + 'negotiation_result': { + 'algorithm': result.algorithm.toJson(), + 'security_level': result.securityLevel, + 'family': result.family.toJson(), + 'hybrid': result.hybrid, + }, + 'peer_public_key': base64Encode(peerPublicKey), + }); + return SessionParams.fromJson(resp); + } +} diff --git a/sdk/flutter/lib/src/crypto/types.dart b/sdk/flutter/lib/src/crypto/types.dart new file mode 100644 index 0000000..75c671a --- /dev/null +++ b/sdk/flutter/lib/src/crypto/types.dart @@ -0,0 +1,658 @@ +/// Synor Crypto SDK Types for Flutter/Dart +/// +/// Quantum-resistant cryptographic types for the Synor blockchain. +library synor_crypto_types; + +import 'dart:convert'; +import 'dart:typed_data'; + +// ============================================================================ +// Enumerations +// ============================================================================ + +/// Network type for address generation +enum Network { + mainnet, + testnet, + devnet; + + String toJson() => name; + static Network fromJson(String json) => Network.values.byName(json); +} + +/// Falcon variant selection +enum FalconVariant { + falcon512, + falcon1024; + + String toJson() => name; + static FalconVariant fromJson(String json) => FalconVariant.values.byName(json); + + /// Returns the signature size for this variant + int get signatureSize => this == falcon512 ? 690 : 1330; + + /// Returns the public key size for this variant + int get publicKeySize => this == falcon512 ? 897 : 1793; + + /// Returns the security level in bits + int get securityLevel => this == falcon512 ? 128 : 256; +} + +/// SPHINCS+ variant selection +enum SphincsVariant { + shake128s, + shake192s, + shake256s; + + String toJson() => name; + static SphincsVariant fromJson(String json) => SphincsVariant.values.byName(json); + + /// Returns the signature size for this variant + int get signatureSize { + switch (this) { + case shake128s: + return 7856; + case shake192s: + return 16224; + case shake256s: + return 29792; + } + } + + /// Returns the security level in bits + int get securityLevel { + switch (this) { + case shake128s: + return 128; + case shake192s: + return 192; + case shake256s: + return 256; + } + } +} + +/// Post-quantum algorithm selection +enum PqAlgorithm { + dilithium3, + falcon512, + falcon1024, + sphincs128s, + sphincs192s, + sphincs256s; + + String toJson() => name; + static PqAlgorithm fromJson(String json) => PqAlgorithm.values.byName(json); +} + +/// Algorithm family classification +enum AlgorithmFamily { + classical, + lattice, + hashBased, + hybrid; + + String toJson() => name; + static AlgorithmFamily fromJson(String json) => AlgorithmFamily.values.byName(json); +} + +// ============================================================================ +// Configuration Types +// ============================================================================ + +/// Crypto SDK configuration +class CryptoConfig { + final String apiKey; + final String endpoint; + final int timeout; + final int retries; + final bool debug; + final Network defaultNetwork; + + const CryptoConfig({ + required this.apiKey, + this.endpoint = 'https://crypto.synor.io/v1', + this.timeout = 30000, + this.retries = 3, + this.debug = false, + this.defaultNetwork = Network.mainnet, + }); +} + +/// Key derivation configuration +class DerivationConfig { + final Uint8List? salt; + final Uint8List? info; + final int outputLength; + + const DerivationConfig({ + this.salt, + this.info, + this.outputLength = 32, + }); +} + +/// Password derivation configuration +class PasswordDerivationConfig { + final Uint8List salt; + final int iterations; + final int outputLength; + + const PasswordDerivationConfig({ + required this.salt, + this.iterations = 100000, + this.outputLength = 32, + }); +} + +/// BIP-44 derivation path +class DerivationPath { + static const int coinType = 0x5359; // Synor coin type + + final int account; + final int change; + final int index; + + const DerivationPath({ + this.account = 0, + this.change = 0, + this.index = 0, + }); + + factory DerivationPath.external(int account, int index) => + DerivationPath(account: account, change: 0, index: index); + + factory DerivationPath.internal(int account, int index) => + DerivationPath(account: account, change: 1, index: index); + + @override + String toString() => "m/44'/$coinType'/$account'/$change/$index"; + + Map toJson() => { + 'account': account, + 'change': change, + 'index': index, + }; +} + +// ============================================================================ +// Key Types +// ============================================================================ + +/// Hybrid public key (Ed25519 + Dilithium3) +class HybridPublicKey { + /// Ed25519 component (32 bytes) + final Uint8List ed25519; + + /// Dilithium3 component (~1952 bytes) + final Uint8List dilithium; + + const HybridPublicKey({ + required this.ed25519, + required this.dilithium, + }); + + /// Returns the total size in bytes + int get size => ed25519.length + dilithium.length; + + factory HybridPublicKey.fromJson(Map json) => HybridPublicKey( + ed25519: base64Decode(json['ed25519'] as String), + dilithium: base64Decode(json['dilithium'] as String), + ); + + Map toJson() => { + 'ed25519': base64Encode(ed25519), + 'dilithium': base64Encode(dilithium), + }; +} + +/// Secret key (master seed) +class SecretKey { + /// Ed25519 seed (32 bytes) + final Uint8List ed25519Seed; + + /// Master seed (64 bytes) + final Uint8List masterSeed; + + const SecretKey({ + required this.ed25519Seed, + required this.masterSeed, + }); + + factory SecretKey.fromJson(Map json) => SecretKey( + ed25519Seed: base64Decode(json['ed25519_seed'] as String), + masterSeed: base64Decode(json['master_seed'] as String), + ); +} + +/// Falcon public key +class FalconPublicKey { + final FalconVariant variant; + final Uint8List bytes; + + const FalconPublicKey({required this.variant, required this.bytes}); + + factory FalconPublicKey.fromJson(Map json) => FalconPublicKey( + variant: FalconVariant.fromJson(json['variant'] as String), + bytes: base64Decode(json['bytes'] as String), + ); +} + +/// Falcon secret key +class FalconSecretKey { + final FalconVariant variant; + final Uint8List bytes; + + const FalconSecretKey({required this.variant, required this.bytes}); + + factory FalconSecretKey.fromJson(Map json) => FalconSecretKey( + variant: FalconVariant.fromJson(json['variant'] as String), + bytes: base64Decode(json['bytes'] as String), + ); +} + +/// SPHINCS+ public key +class SphincsPublicKey { + final SphincsVariant variant; + final Uint8List bytes; + + const SphincsPublicKey({required this.variant, required this.bytes}); + + factory SphincsPublicKey.fromJson(Map json) => SphincsPublicKey( + variant: SphincsVariant.fromJson(json['variant'] as String), + bytes: base64Decode(json['bytes'] as String), + ); +} + +/// SPHINCS+ secret key +class SphincsSecretKey { + final SphincsVariant variant; + final Uint8List bytes; + + const SphincsSecretKey({required this.variant, required this.bytes}); + + factory SphincsSecretKey.fromJson(Map json) => SphincsSecretKey( + variant: SphincsVariant.fromJson(json['variant'] as String), + bytes: base64Decode(json['bytes'] as String), + ); +} + +// ============================================================================ +// Signature Types +// ============================================================================ + +/// Hybrid signature (Ed25519 + Dilithium3) +class HybridSignature { + /// Ed25519 component (64 bytes) + final Uint8List ed25519; + + /// Dilithium3 component (~3293 bytes) + final Uint8List dilithium; + + const HybridSignature({ + required this.ed25519, + required this.dilithium, + }); + + /// Returns the total size in bytes + int get size => ed25519.length + dilithium.length; + + /// Serializes to bytes + Uint8List toBytes() { + final result = Uint8List(size); + result.setAll(0, ed25519); + result.setAll(ed25519.length, dilithium); + return result; + } + + factory HybridSignature.fromBytes(Uint8List data) { + if (data.length < 64) { + throw ArgumentError('Invalid signature length'); + } + return HybridSignature( + ed25519: Uint8List.fromList(data.sublist(0, 64)), + dilithium: Uint8List.fromList(data.sublist(64)), + ); + } + + factory HybridSignature.fromJson(Map json) => HybridSignature( + ed25519: base64Decode(json['ed25519'] as String), + dilithium: base64Decode(json['dilithium'] as String), + ); + + Map toJson() => { + 'ed25519': base64Encode(ed25519), + 'dilithium': base64Encode(dilithium), + }; +} + +/// Falcon signature +class FalconSignature { + final FalconVariant variant; + final Uint8List bytes; + + const FalconSignature({required this.variant, required this.bytes}); + + int get size => bytes.length; + + factory FalconSignature.fromJson(Map json) => FalconSignature( + variant: FalconVariant.fromJson(json['variant'] as String), + bytes: base64Decode(json['signature'] as String), + ); +} + +/// SPHINCS+ signature +class SphincsSignature { + final SphincsVariant variant; + final Uint8List bytes; + + const SphincsSignature({required this.variant, required this.bytes}); + + int get size => bytes.length; + + factory SphincsSignature.fromJson(Map json) => SphincsSignature( + variant: SphincsVariant.fromJson(json['variant'] as String), + bytes: base64Decode(json['signature'] as String), + ); +} + +// ============================================================================ +// Keypair Types +// ============================================================================ + +/// Hybrid keypair (Ed25519 + Dilithium3) +class HybridKeypair { + final HybridPublicKey publicKey; + final SecretKey secretKey; + final Map _addresses; + + const HybridKeypair({ + required this.publicKey, + required this.secretKey, + Map addresses = const {}, + }) : _addresses = addresses; + + /// Returns the address for a network + String address(Network network) => _addresses[network] ?? ''; + + factory HybridKeypair.fromJson(Map json) { + final addressesJson = json['addresses'] as Map? ?? {}; + final addresses = {}; + addressesJson.forEach((k, v) { + try { + addresses[Network.fromJson(k)] = v as String; + } catch (_) {} + }); + + return HybridKeypair( + publicKey: HybridPublicKey.fromJson(json['public_key'] as Map), + secretKey: SecretKey.fromJson(json['secret_key'] as Map), + addresses: addresses, + ); + } +} + +/// Falcon keypair +class FalconKeypair { + final FalconVariant variant; + final FalconPublicKey publicKey; + final FalconSecretKey secretKey; + + const FalconKeypair({ + required this.variant, + required this.publicKey, + required this.secretKey, + }); + + factory FalconKeypair.fromJson(Map json) => FalconKeypair( + variant: FalconVariant.fromJson(json['variant'] as String), + publicKey: FalconPublicKey.fromJson(json['public_key'] as Map), + secretKey: FalconSecretKey.fromJson(json['secret_key'] as Map), + ); +} + +/// SPHINCS+ keypair +class SphincsKeypair { + final SphincsVariant variant; + final SphincsPublicKey publicKey; + final SphincsSecretKey secretKey; + + const SphincsKeypair({ + required this.variant, + required this.publicKey, + required this.secretKey, + }); + + factory SphincsKeypair.fromJson(Map json) => SphincsKeypair( + variant: SphincsVariant.fromJson(json['variant'] as String), + publicKey: SphincsPublicKey.fromJson(json['public_key'] as Map), + secretKey: SphincsSecretKey.fromJson(json['secret_key'] as Map), + ); +} + +// ============================================================================ +// Mnemonic Types +// ============================================================================ + +/// BIP-39 mnemonic phrase +class Mnemonic { + final String phrase; + final List words; + final int wordCount; + final Uint8List entropy; + + const Mnemonic({ + required this.phrase, + required this.words, + required this.wordCount, + required this.entropy, + }); + + factory Mnemonic.fromJson(Map json) => Mnemonic( + phrase: json['phrase'] as String, + words: List.from(json['words'] ?? (json['phrase'] as String).split(' ')), + wordCount: json['word_count'] as int? ?? (json['phrase'] as String).split(' ').length, + entropy: json['entropy'] != null ? base64Decode(json['entropy'] as String) : Uint8List(0), + ); +} + +/// Mnemonic validation result +class MnemonicValidation { + final bool valid; + final String? error; + + const MnemonicValidation({required this.valid, this.error}); + + factory MnemonicValidation.fromJson(Map json) => MnemonicValidation( + valid: json['valid'] as bool, + error: json['error'] as String?, + ); +} + +// ============================================================================ +// Address Types +// ============================================================================ + +/// Blockchain address +class Address { + final String address; + final Network network; + final Uint8List pubkeyHash; + + const Address({ + required this.address, + required this.network, + required this.pubkeyHash, + }); + + factory Address.fromJson(Map json) => Address( + address: json['address'] as String, + network: Network.fromJson(json['network'] as String), + pubkeyHash: json['pubkey_hash'] != null + ? base64Decode(json['pubkey_hash'] as String) + : Uint8List(0), + ); +} + +// ============================================================================ +// Hash Types +// ============================================================================ + +/// 256-bit hash +class Hash256 { + final Uint8List bytes; + final String hex; + + const Hash256({required this.bytes, required this.hex}); + + factory Hash256.fromJson(Map json) { + final hexStr = json['hash'] as String; + return Hash256( + bytes: _hexDecode(hexStr), + hex: hexStr, + ); + } +} + +Uint8List _hexDecode(String hex) { + final result = Uint8List(hex.length ~/ 2); + for (var i = 0; i < result.length; i++) { + result[i] = int.parse(hex.substring(i * 2, i * 2 + 2), radix: 16); + } + return result; +} + +// ============================================================================ +// Negotiation Types +// ============================================================================ + +/// Algorithm capabilities for negotiation +class AlgorithmCapabilities { + final List pqAlgorithms; + final bool classical; + final bool hybrid; + final PqAlgorithm? preferred; + + const AlgorithmCapabilities({ + required this.pqAlgorithms, + this.classical = true, + this.hybrid = true, + this.preferred, + }); + + factory AlgorithmCapabilities.fromJson(Map json) => AlgorithmCapabilities( + pqAlgorithms: (json['pq_algorithms'] as List) + .map((e) => PqAlgorithm.fromJson(e as String)) + .toList(), + classical: json['classical'] as bool? ?? true, + hybrid: json['hybrid'] as bool? ?? true, + preferred: json['preferred'] != null + ? PqAlgorithm.fromJson(json['preferred'] as String) + : null, + ); +} + +/// Negotiation policy +class NegotiationPolicy { + final int minSecurityLevel; + final bool preferCompact; + final bool allowClassical; + final List requiredFamilies; + + const NegotiationPolicy({ + this.minSecurityLevel = 128, + this.preferCompact = false, + this.allowClassical = false, + this.requiredFamilies = const [], + }); + + Map toJson() => { + 'min_security_level': minSecurityLevel, + 'prefer_compact': preferCompact, + 'allow_classical': allowClassical, + 'required_families': requiredFamilies.map((e) => e.toJson()).toList(), + }; +} + +/// Negotiation result +class NegotiationResult { + final PqAlgorithm algorithm; + final int securityLevel; + final AlgorithmFamily family; + final bool hybrid; + + const NegotiationResult({ + required this.algorithm, + required this.securityLevel, + required this.family, + required this.hybrid, + }); + + factory NegotiationResult.fromJson(Map json) => NegotiationResult( + algorithm: PqAlgorithm.fromJson(json['algorithm'] as String), + securityLevel: json['security_level'] as int, + family: AlgorithmFamily.fromJson(json['family'] as String), + hybrid: json['hybrid'] as bool, + ); +} + +/// Session parameters after negotiation +class SessionParams { + final PqAlgorithm algorithm; + final Uint8List? sessionKey; + final int? expiresAt; + + const SessionParams({ + required this.algorithm, + this.sessionKey, + this.expiresAt, + }); + + factory SessionParams.fromJson(Map json) => SessionParams( + algorithm: PqAlgorithm.fromJson(json['algorithm'] as String), + sessionKey: json['session_key'] != null + ? base64Decode(json['session_key'] as String) + : null, + expiresAt: json['expires_at'] as int?, + ); +} + +// ============================================================================ +// Error Types +// ============================================================================ + +/// Crypto operation error +class CryptoException implements Exception { + final String message; + final String? code; + + const CryptoException(this.message, [this.code]); + + @override + String toString() => + code != null ? 'CryptoException: $message (code: $code)' : 'CryptoException: $message'; +} + +// ============================================================================ +// Constants +// ============================================================================ + +abstract class CryptoConstants { + static const int ed25519PublicKeySize = 32; + static const int ed25519SecretKeySize = 32; + static const int ed25519SignatureSize = 64; + static const int dilithium3PublicKeySize = 1952; + static const int dilithium3SignatureSize = 3293; + static const int hybridSignatureSize = 64 + 3293; + static const int falcon512SignatureSize = 690; + static const int falcon512PublicKeySize = 897; + static const int falcon1024SignatureSize = 1330; + static const int falcon1024PublicKeySize = 1793; + static const int sphincs128sSignatureSize = 7856; + static const int sphincs192sSignatureSize = 16224; + static const int sphincs256sSignatureSize = 29792; + static const int coinType = 0x5359; + static const int minPbkdf2Iterations = 10000; + static const int minSaltLength = 8; + static const String defaultEndpoint = 'https://crypto.synor.io/v1'; +} diff --git a/sdk/go/crypto/client.go b/sdk/go/crypto/client.go new file mode 100644 index 0000000..994ffe3 --- /dev/null +++ b/sdk/go/crypto/client.go @@ -0,0 +1,691 @@ +// Package crypto provides quantum-resistant cryptographic primitives for the Synor blockchain. +package crypto + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "time" +) + +// SynorCrypto is the main client for the Crypto SDK +type SynorCrypto struct { + config Config + client *http.Client + closed bool + + Mnemonic *MnemonicClient + Keypairs *KeypairClient + Signing *SigningClient + Falcon *FalconClient + Sphincs *SphincsClient + KDF *KDFClient + Hash *HashClient + Negotiation *NegotiationClient +} + +// New creates a new SynorCrypto client +func New(config Config) *SynorCrypto { + if config.Endpoint == "" { + config.Endpoint = DefaultEndpoint + } + if config.Timeout == 0 { + config.Timeout = 30000 + } + if config.Retries == 0 { + config.Retries = 3 + } + if config.DefaultNetwork == "" { + config.DefaultNetwork = NetworkMainnet + } + + c := &SynorCrypto{ + config: config, + client: &http.Client{ + Timeout: time.Duration(config.Timeout) * time.Millisecond, + }, + } + + c.Mnemonic = &MnemonicClient{crypto: c} + c.Keypairs = &KeypairClient{crypto: c} + c.Signing = &SigningClient{crypto: c} + c.Falcon = &FalconClient{crypto: c} + c.Sphincs = &SphincsClient{crypto: c} + c.KDF = &KDFClient{crypto: c} + c.Hash = &HashClient{crypto: c} + c.Negotiation = &NegotiationClient{crypto: c} + + return c +} + +// DefaultNetwork returns the default network +func (c *SynorCrypto) DefaultNetwork() Network { + return c.config.DefaultNetwork +} + +// HealthCheck checks if the service is healthy +func (c *SynorCrypto) HealthCheck(ctx context.Context) (bool, error) { + var result map[string]interface{} + if err := c.get(ctx, "/health", &result); err != nil { + return false, nil + } + status, _ := result["status"].(string) + return status == "healthy", nil +} + +// GetInfo returns service information +func (c *SynorCrypto) GetInfo(ctx context.Context) (map[string]interface{}, error) { + var result map[string]interface{} + err := c.get(ctx, "/info", &result) + return result, err +} + +// Close closes the client +func (c *SynorCrypto) Close() { + c.closed = true +} + +// IsClosed returns true if the client is closed +func (c *SynorCrypto) IsClosed() bool { + return c.closed +} + +func (c *SynorCrypto) get(ctx context.Context, path string, result interface{}) error { + if c.closed { + return NewCryptoError("Client has been closed", "CLIENT_CLOSED") + } + + req, err := http.NewRequestWithContext(ctx, "GET", c.config.Endpoint+path, nil) + if err != nil { + return err + } + c.setHeaders(req) + + resp, err := c.client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + return c.handleResponse(resp, result) +} + +func (c *SynorCrypto) post(ctx context.Context, path string, body interface{}, result interface{}) error { + if c.closed { + return NewCryptoError("Client has been closed", "CLIENT_CLOSED") + } + + var reqBody io.Reader + if body != nil { + jsonBody, err := json.Marshal(body) + if err != nil { + return err + } + reqBody = bytes.NewBuffer(jsonBody) + } + + req, err := http.NewRequestWithContext(ctx, "POST", c.config.Endpoint+path, reqBody) + if err != nil { + return err + } + c.setHeaders(req) + + resp, err := c.client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + return c.handleResponse(resp, result) +} + +func (c *SynorCrypto) setHeaders(req *http.Request) { + req.Header.Set("Authorization", "Bearer "+c.config.APIKey) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-SDK-Version", "go/0.1.0") +} + +func (c *SynorCrypto) handleResponse(resp *http.Response, result interface{}) error { + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode >= 400 { + var errResp map[string]interface{} + json.Unmarshal(body, &errResp) + msg := fmt.Sprintf("HTTP %d", resp.StatusCode) + code := "" + if m, ok := errResp["message"].(string); ok { + msg = m + } + if c, ok := errResp["code"].(string); ok { + code = c + } + return NewCryptoError(msg, code) + } + + return json.Unmarshal(body, result) +} + +// ============================================================================ +// Mnemonic Client +// ============================================================================ + +// MnemonicClient handles mnemonic operations +type MnemonicClient struct { + crypto *SynorCrypto +} + +// Generate creates a new random mnemonic +func (c *MnemonicClient) Generate(ctx context.Context, wordCount int) (*Mnemonic, error) { + var result mnemonicResponse + err := c.crypto.post(ctx, "/mnemonic/generate", map[string]int{"word_count": wordCount}, &result) + if err != nil { + return nil, err + } + return result.toMnemonic() +} + +// FromPhrase creates a mnemonic from a phrase +func (c *MnemonicClient) FromPhrase(ctx context.Context, phrase string) (*Mnemonic, error) { + var result mnemonicResponse + err := c.crypto.post(ctx, "/mnemonic/from-phrase", map[string]string{"phrase": phrase}, &result) + if err != nil { + return nil, err + } + return result.toMnemonic() +} + +// FromEntropy creates a mnemonic from entropy +func (c *MnemonicClient) FromEntropy(ctx context.Context, entropy []byte) (*Mnemonic, error) { + var result mnemonicResponse + err := c.crypto.post(ctx, "/mnemonic/from-entropy", map[string]string{ + "entropy": EncodeBase64(entropy), + }, &result) + if err != nil { + return nil, err + } + return result.toMnemonic() +} + +// Validate checks if a phrase is valid +func (c *MnemonicClient) Validate(ctx context.Context, phrase string) (*MnemonicValidation, error) { + var result MnemonicValidation + err := c.crypto.post(ctx, "/mnemonic/validate", map[string]string{"phrase": phrase}, &result) + return &result, err +} + +// ToSeed derives a seed from a mnemonic +func (c *MnemonicClient) ToSeed(ctx context.Context, phrase string, passphrase string) ([]byte, error) { + var result struct { + Seed string `json:"seed"` + } + err := c.crypto.post(ctx, "/mnemonic/to-seed", map[string]string{ + "phrase": phrase, + "passphrase": passphrase, + }, &result) + if err != nil { + return nil, err + } + return DecodeBase64(result.Seed) +} + +// SuggestWords suggests completions for a partial word +func (c *MnemonicClient) SuggestWords(ctx context.Context, partial string, limit int) ([]string, error) { + var result struct { + Suggestions []string `json:"suggestions"` + } + err := c.crypto.post(ctx, "/mnemonic/suggest", map[string]interface{}{ + "partial": partial, + "limit": limit, + }, &result) + return result.Suggestions, err +} + +type mnemonicResponse struct { + Phrase string `json:"phrase"` + Words []string `json:"words"` + WordCount int `json:"word_count"` + Entropy string `json:"entropy"` +} + +func (r *mnemonicResponse) toMnemonic() (*Mnemonic, error) { + entropy, _ := DecodeBase64(r.Entropy) + return &Mnemonic{ + Phrase: r.Phrase, + Words: r.Words, + WordCount: r.WordCount, + Entropy: entropy, + }, nil +} + +// ============================================================================ +// Keypair Client +// ============================================================================ + +// KeypairClient handles keypair operations +type KeypairClient struct { + crypto *SynorCrypto +} + +// Generate creates a new random keypair +func (c *KeypairClient) Generate(ctx context.Context) (*HybridKeypair, error) { + var result keypairResponse + err := c.crypto.post(ctx, "/keypair/generate", nil, &result) + if err != nil { + return nil, err + } + return result.toKeypair() +} + +// FromMnemonic creates a keypair from a mnemonic +func (c *KeypairClient) FromMnemonic(ctx context.Context, phrase string, passphrase string) (*HybridKeypair, error) { + var result keypairResponse + err := c.crypto.post(ctx, "/keypair/from-mnemonic", map[string]string{ + "phrase": phrase, + "passphrase": passphrase, + }, &result) + if err != nil { + return nil, err + } + return result.toKeypair() +} + +// FromSeed creates a keypair from a seed +func (c *KeypairClient) FromSeed(ctx context.Context, seed []byte) (*HybridKeypair, error) { + var result keypairResponse + err := c.crypto.post(ctx, "/keypair/from-seed", map[string]string{ + "seed": EncodeBase64(seed), + }, &result) + if err != nil { + return nil, err + } + return result.toKeypair() +} + +// GetAddress gets the address for a public key +func (c *KeypairClient) GetAddress(ctx context.Context, pk *HybridPublicKey, network Network) (*Address, error) { + var result Address + err := c.crypto.post(ctx, "/keypair/address", map[string]interface{}{ + "public_key": map[string]string{ + "ed25519": EncodeBase64(pk.Ed25519), + "dilithium": EncodeBase64(pk.Dilithium), + }, + "network": network, + }, &result) + return &result, err +} + +type keypairResponse struct { + PublicKey struct { + Ed25519 string `json:"ed25519"` + Dilithium string `json:"dilithium"` + } `json:"public_key"` + SecretKey struct { + Ed25519Seed string `json:"ed25519_seed"` + MasterSeed string `json:"master_seed"` + } `json:"secret_key"` + Addresses map[string]string `json:"addresses"` +} + +func (r *keypairResponse) toKeypair() (*HybridKeypair, error) { + ed25519, _ := DecodeBase64(r.PublicKey.Ed25519) + dilithium, _ := DecodeBase64(r.PublicKey.Dilithium) + ed25519Seed, _ := DecodeBase64(r.SecretKey.Ed25519Seed) + masterSeed, _ := DecodeBase64(r.SecretKey.MasterSeed) + + addresses := make(map[Network]string) + for k, v := range r.Addresses { + addresses[Network(k)] = v + } + + return &HybridKeypair{ + PublicKey: &HybridPublicKey{ + Ed25519: ed25519, + Dilithium: dilithium, + }, + SecretKey: &SecretKey{ + Ed25519Seed: ed25519Seed, + MasterSeed: masterSeed, + }, + addresses: addresses, + }, nil +} + +// ============================================================================ +// Signing Client +// ============================================================================ + +// SigningClient handles signing operations +type SigningClient struct { + crypto *SynorCrypto +} + +// Sign signs a message with a hybrid keypair +func (c *SigningClient) Sign(ctx context.Context, kp *HybridKeypair, message []byte) (*HybridSignature, error) { + var result struct { + Ed25519 string `json:"ed25519"` + Dilithium string `json:"dilithium"` + } + err := c.crypto.post(ctx, "/sign/hybrid", map[string]interface{}{ + "secret_key": map[string]string{ + "ed25519_seed": EncodeBase64(kp.SecretKey.Ed25519Seed), + "master_seed": EncodeBase64(kp.SecretKey.MasterSeed), + }, + "message": EncodeBase64(message), + }, &result) + if err != nil { + return nil, err + } + + ed25519, _ := DecodeBase64(result.Ed25519) + dilithium, _ := DecodeBase64(result.Dilithium) + return &HybridSignature{ + Ed25519: ed25519, + Dilithium: dilithium, + }, nil +} + +// Verify verifies a hybrid signature +func (c *SigningClient) Verify(ctx context.Context, pk *HybridPublicKey, message []byte, sig *HybridSignature) (bool, error) { + var result struct { + Valid bool `json:"valid"` + } + err := c.crypto.post(ctx, "/sign/verify", map[string]interface{}{ + "public_key": map[string]string{ + "ed25519": EncodeBase64(pk.Ed25519), + "dilithium": EncodeBase64(pk.Dilithium), + }, + "message": EncodeBase64(message), + "signature": map[string]string{ + "ed25519": EncodeBase64(sig.Ed25519), + "dilithium": EncodeBase64(sig.Dilithium), + }, + }, &result) + return result.Valid, err +} + +// SignEd25519 signs with Ed25519 only +func (c *SigningClient) SignEd25519(ctx context.Context, secretKey []byte, message []byte) ([]byte, error) { + var result struct { + Signature string `json:"signature"` + } + err := c.crypto.post(ctx, "/sign/ed25519", map[string]string{ + "secret_key": EncodeBase64(secretKey), + "message": EncodeBase64(message), + }, &result) + if err != nil { + return nil, err + } + return DecodeBase64(result.Signature) +} + +// ============================================================================ +// Falcon Client +// ============================================================================ + +// FalconClient handles Falcon operations +type FalconClient struct { + crypto *SynorCrypto +} + +// Generate creates a Falcon keypair +func (c *FalconClient) Generate(ctx context.Context, variant FalconVariant) (*FalconKeypair, error) { + var result map[string]interface{} + err := c.crypto.post(ctx, "/falcon/generate", map[string]string{"variant": string(variant)}, &result) + if err != nil { + return nil, err + } + // Parse response into FalconKeypair + return parseFalconKeypair(result, variant) +} + +// Sign signs with Falcon +func (c *FalconClient) Sign(ctx context.Context, kp *FalconKeypair, message []byte) (*FalconSignature, error) { + var result struct { + Signature string `json:"signature"` + Variant string `json:"variant"` + } + err := c.crypto.post(ctx, "/falcon/sign", map[string]string{ + "variant": string(kp.Variant), + "secret_key": EncodeBase64(kp.SecretKey.Bytes), + "message": EncodeBase64(message), + }, &result) + if err != nil { + return nil, err + } + sigBytes, _ := DecodeBase64(result.Signature) + return &FalconSignature{ + Variant: FalconVariant(result.Variant), + Bytes: sigBytes, + }, nil +} + +// Verify verifies a Falcon signature +func (c *FalconClient) Verify(ctx context.Context, publicKey []byte, message []byte, sig *FalconSignature) (bool, error) { + var result struct { + Valid bool `json:"valid"` + } + err := c.crypto.post(ctx, "/falcon/verify", map[string]interface{}{ + "variant": sig.Variant, + "public_key": EncodeBase64(publicKey), + "message": EncodeBase64(message), + "signature": EncodeBase64(sig.Bytes), + }, &result) + return result.Valid, err +} + +func parseFalconKeypair(data map[string]interface{}, variant FalconVariant) (*FalconKeypair, error) { + pk := data["public_key"].(map[string]interface{}) + sk := data["secret_key"].(map[string]interface{}) + pkBytes, _ := DecodeBase64(pk["bytes"].(string)) + skBytes, _ := DecodeBase64(sk["bytes"].(string)) + return &FalconKeypair{ + Variant: variant, + PublicKey: &FalconPublicKey{Variant: variant, Bytes: pkBytes}, + SecretKey: &FalconSecretKey{Variant: variant, Bytes: skBytes}, + }, nil +} + +// ============================================================================ +// SPHINCS+ Client +// ============================================================================ + +// SphincsClient handles SPHINCS+ operations +type SphincsClient struct { + crypto *SynorCrypto +} + +// Generate creates a SPHINCS+ keypair +func (c *SphincsClient) Generate(ctx context.Context, variant SphincsVariant) (*SphincsKeypair, error) { + var result map[string]interface{} + err := c.crypto.post(ctx, "/sphincs/generate", map[string]string{"variant": string(variant)}, &result) + if err != nil { + return nil, err + } + return parseSphincsKeypair(result, variant) +} + +// Sign signs with SPHINCS+ +func (c *SphincsClient) Sign(ctx context.Context, kp *SphincsKeypair, message []byte) (*SphincsSignature, error) { + var result struct { + Signature string `json:"signature"` + Variant string `json:"variant"` + } + err := c.crypto.post(ctx, "/sphincs/sign", map[string]string{ + "variant": string(kp.Variant), + "secret_key": EncodeBase64(kp.SecretKey.Bytes), + "message": EncodeBase64(message), + }, &result) + if err != nil { + return nil, err + } + sigBytes, _ := DecodeBase64(result.Signature) + return &SphincsSignature{ + Variant: SphincsVariant(result.Variant), + Bytes: sigBytes, + }, nil +} + +// Verify verifies a SPHINCS+ signature +func (c *SphincsClient) Verify(ctx context.Context, publicKey []byte, message []byte, sig *SphincsSignature) (bool, error) { + var result struct { + Valid bool `json:"valid"` + } + err := c.crypto.post(ctx, "/sphincs/verify", map[string]interface{}{ + "variant": sig.Variant, + "public_key": EncodeBase64(publicKey), + "message": EncodeBase64(message), + "signature": EncodeBase64(sig.Bytes), + }, &result) + return result.Valid, err +} + +func parseSphincsKeypair(data map[string]interface{}, variant SphincsVariant) (*SphincsKeypair, error) { + pk := data["public_key"].(map[string]interface{}) + sk := data["secret_key"].(map[string]interface{}) + pkBytes, _ := DecodeBase64(pk["bytes"].(string)) + skBytes, _ := DecodeBase64(sk["bytes"].(string)) + return &SphincsKeypair{ + Variant: variant, + PublicKey: &SphincsPublicKey{Variant: variant, Bytes: pkBytes}, + SecretKey: &SphincsSecretKey{Variant: variant, Bytes: skBytes}, + }, nil +} + +// ============================================================================ +// KDF Client +// ============================================================================ + +// KDFClient handles key derivation +type KDFClient struct { + crypto *SynorCrypto +} + +// DeriveKey derives a key using HKDF +func (c *KDFClient) DeriveKey(ctx context.Context, seed []byte, config *DerivationConfig) ([]byte, error) { + body := map[string]interface{}{ + "seed": EncodeBase64(seed), + "output_length": config.OutputLength, + } + if config.Salt != nil { + body["salt"] = EncodeBase64(config.Salt) + } + if config.Info != nil { + body["info"] = EncodeBase64(config.Info) + } + + var result struct { + Key string `json:"key"` + } + err := c.crypto.post(ctx, "/kdf/hkdf", body, &result) + if err != nil { + return nil, err + } + return DecodeBase64(result.Key) +} + +// DeriveFromPassword derives a key from a password +func (c *KDFClient) DeriveFromPassword(ctx context.Context, password []byte, config *PasswordDerivationConfig) ([]byte, error) { + var result struct { + Key string `json:"key"` + } + err := c.crypto.post(ctx, "/kdf/pbkdf2", map[string]interface{}{ + "password": EncodeBase64(password), + "salt": EncodeBase64(config.Salt), + "iterations": config.Iterations, + "output_length": config.OutputLength, + }, &result) + if err != nil { + return nil, err + } + return DecodeBase64(result.Key) +} + +// DeriveChildKey derives a child key +func (c *KDFClient) DeriveChildKey(ctx context.Context, parentKey, chainCode []byte, index int) ([]byte, []byte, error) { + var result struct { + Key string `json:"key"` + ChainCode string `json:"chain_code"` + } + err := c.crypto.post(ctx, "/kdf/child", map[string]interface{}{ + "parent_key": EncodeBase64(parentKey), + "chain_code": EncodeBase64(chainCode), + "index": index, + }, &result) + if err != nil { + return nil, nil, err + } + key, _ := DecodeBase64(result.Key) + cc, _ := DecodeBase64(result.ChainCode) + return key, cc, nil +} + +// ============================================================================ +// Hash Client +// ============================================================================ + +// HashClient handles hashing operations +type HashClient struct { + crypto *SynorCrypto +} + +// SHA3_256 computes SHA3-256 hash +func (c *HashClient) SHA3_256(ctx context.Context, data []byte) (*Hash256, error) { + var result struct { + Hash string `json:"hash"` + } + err := c.crypto.post(ctx, "/hash/sha3-256", map[string]string{ + "data": EncodeBase64(data), + }, &result) + if err != nil { + return nil, err + } + hashBytes, _ := DecodeBase64(result.Hash) + return &Hash256{Bytes: hashBytes, Hex: result.Hash}, nil +} + +// Blake3 computes BLAKE3 hash +func (c *HashClient) Blake3(ctx context.Context, data []byte) (*Hash256, error) { + var result struct { + Hash string `json:"hash"` + } + err := c.crypto.post(ctx, "/hash/blake3", map[string]string{ + "data": EncodeBase64(data), + }, &result) + if err != nil { + return nil, err + } + hashBytes, _ := DecodeBase64(result.Hash) + return &Hash256{Bytes: hashBytes, Hex: result.Hash}, nil +} + +// ============================================================================ +// Negotiation Client +// ============================================================================ + +// NegotiationClient handles algorithm negotiation +type NegotiationClient struct { + crypto *SynorCrypto +} + +// GetCapabilities returns local capabilities +func (c *NegotiationClient) GetCapabilities(ctx context.Context) (*AlgorithmCapabilities, error) { + var result AlgorithmCapabilities + err := c.crypto.get(ctx, "/negotiation/capabilities", &result) + return &result, err +} + +// Negotiate negotiates with a peer +func (c *NegotiationClient) Negotiate(ctx context.Context, peerCaps *AlgorithmCapabilities, policy *NegotiationPolicy) (*NegotiationResult, error) { + var result NegotiationResult + err := c.crypto.post(ctx, "/negotiation/negotiate", map[string]interface{}{ + "peer_capabilities": peerCaps, + "policy": policy, + }, &result) + return &result, err +} diff --git a/sdk/go/crypto/types.go b/sdk/go/crypto/types.go new file mode 100644 index 0000000..a744dca --- /dev/null +++ b/sdk/go/crypto/types.go @@ -0,0 +1,479 @@ +// Package crypto provides quantum-resistant cryptographic primitives for the Synor blockchain. +package crypto + +import ( + "encoding/base64" + "encoding/hex" + "fmt" +) + +// ============================================================================ +// Enumerations +// ============================================================================ + +// Network type for address generation +type Network string + +const ( + NetworkMainnet Network = "mainnet" + NetworkTestnet Network = "testnet" + NetworkDevnet Network = "devnet" +) + +// FalconVariant selection +type FalconVariant string + +const ( + Falcon512 FalconVariant = "falcon512" // 128-bit security, ~690 byte signatures + Falcon1024 FalconVariant = "falcon1024" // 256-bit security, ~1330 byte signatures +) + +// SphincsVariant selection +type SphincsVariant string + +const ( + Shake128s SphincsVariant = "shake128s" // 128-bit security, ~7.8KB signatures + Shake192s SphincsVariant = "shake192s" // 192-bit security, ~16KB signatures + Shake256s SphincsVariant = "shake256s" // 256-bit security, ~30KB signatures +) + +// PqAlgorithm post-quantum algorithm selection +type PqAlgorithm string + +const ( + Dilithium3 PqAlgorithm = "dilithium3" + PqFalcon512 PqAlgorithm = "falcon512" + PqFalcon1024 PqAlgorithm = "falcon1024" + Sphincs128s PqAlgorithm = "sphincs128s" + Sphincs192s PqAlgorithm = "sphincs192s" + Sphincs256s PqAlgorithm = "sphincs256s" +) + +// AlgorithmFamily classification +type AlgorithmFamily string + +const ( + Classical AlgorithmFamily = "classical" + Lattice AlgorithmFamily = "lattice" + HashBased AlgorithmFamily = "hash_based" + Hybrid AlgorithmFamily = "hybrid" +) + +// ============================================================================ +// Configuration Types +// ============================================================================ + +// Config for the crypto SDK +type Config struct { + APIKey string + Endpoint string + Timeout int // milliseconds + Retries int + Debug bool + DefaultNetwork Network +} + +// DefaultConfig returns a config with sensible defaults +func DefaultConfig(apiKey string) Config { + return Config{ + APIKey: apiKey, + Endpoint: "https://crypto.synor.io/v1", + Timeout: 30000, + Retries: 3, + Debug: false, + DefaultNetwork: NetworkMainnet, + } +} + +// DerivationConfig for key derivation +type DerivationConfig struct { + Salt []byte + Info []byte + OutputLength int +} + +// PasswordDerivationConfig for password-based key derivation +type PasswordDerivationConfig struct { + Salt []byte + Iterations int + OutputLength int +} + +// DerivationPath BIP-44 style path +type DerivationPath struct { + Account int + Change int + Index int +} + +// CoinType for Synor in BIP-44 +const CoinType = 0x5359 + +// String formats the derivation path +func (p DerivationPath) String() string { + return fmt.Sprintf("m/44'/%d'/%d'/%d/%d", CoinType, p.Account, p.Change, p.Index) +} + +// ExternalPath creates a path for external addresses +func ExternalPath(account, index int) DerivationPath { + return DerivationPath{Account: account, Change: 0, Index: index} +} + +// InternalPath creates a path for internal (change) addresses +func InternalPath(account, index int) DerivationPath { + return DerivationPath{Account: account, Change: 1, Index: index} +} + +// ============================================================================ +// Key Types +// ============================================================================ + +// HybridPublicKey combines Ed25519 and Dilithium3 +type HybridPublicKey struct { + Ed25519 []byte // 32 bytes + Dilithium []byte // ~1952 bytes +} + +// Size returns the total key size +func (pk *HybridPublicKey) Size() int { + return len(pk.Ed25519) + len(pk.Dilithium) +} + +// SecretKey holds the master seed +type SecretKey struct { + Ed25519Seed []byte // 32 bytes + MasterSeed []byte // 64 bytes +} + +// FalconPublicKey for Falcon signatures +type FalconPublicKey struct { + Variant FalconVariant + Bytes []byte +} + +// FalconSecretKey for Falcon signatures +type FalconSecretKey struct { + Variant FalconVariant + Bytes []byte +} + +// SphincsPublicKey for SPHINCS+ signatures +type SphincsPublicKey struct { + Variant SphincsVariant + Bytes []byte +} + +// SphincsSecretKey for SPHINCS+ signatures +type SphincsSecretKey struct { + Variant SphincsVariant + Bytes []byte +} + +// ============================================================================ +// Signature Types +// ============================================================================ + +// HybridSignature combines Ed25519 and Dilithium3 signatures +type HybridSignature struct { + Ed25519 []byte // 64 bytes + Dilithium []byte // ~3293 bytes +} + +// Size returns the total signature size +func (s *HybridSignature) Size() int { + return len(s.Ed25519) + len(s.Dilithium) +} + +// ToBytes serializes the signature +func (s *HybridSignature) ToBytes() []byte { + result := make([]byte, len(s.Ed25519)+len(s.Dilithium)) + copy(result, s.Ed25519) + copy(result[len(s.Ed25519):], s.Dilithium) + return result +} + +// HybridSignatureFromBytes deserializes a signature +func HybridSignatureFromBytes(data []byte) (*HybridSignature, error) { + if len(data) < 64 { + return nil, fmt.Errorf("invalid signature length: %d", len(data)) + } + return &HybridSignature{ + Ed25519: data[:64], + Dilithium: data[64:], + }, nil +} + +// FalconSignature for Falcon +type FalconSignature struct { + Variant FalconVariant + Bytes []byte +} + +// Size returns the signature size +func (s *FalconSignature) Size() int { + return len(s.Bytes) +} + +// SphincsSignature for SPHINCS+ +type SphincsSignature struct { + Variant SphincsVariant + Bytes []byte +} + +// Size returns the signature size +func (s *SphincsSignature) Size() int { + return len(s.Bytes) +} + +// ============================================================================ +// Keypair Types +// ============================================================================ + +// HybridKeypair combines Ed25519 and Dilithium3 +type HybridKeypair struct { + PublicKey *HybridPublicKey + SecretKey *SecretKey + addresses map[Network]string +} + +// Address returns the address for a network +func (kp *HybridKeypair) Address(network Network) string { + if kp.addresses == nil { + return "" + } + return kp.addresses[network] +} + +// FalconKeypair for Falcon signatures +type FalconKeypair struct { + Variant FalconVariant + PublicKey *FalconPublicKey + SecretKey *FalconSecretKey +} + +// SphincsKeypair for SPHINCS+ signatures +type SphincsKeypair struct { + Variant SphincsVariant + PublicKey *SphincsPublicKey + SecretKey *SphincsSecretKey +} + +// ============================================================================ +// Mnemonic Types +// ============================================================================ + +// Mnemonic BIP-39 phrase +type Mnemonic struct { + Phrase string + Words []string + WordCount int + Entropy []byte +} + +// MnemonicValidation result +type MnemonicValidation struct { + Valid bool + Error string +} + +// ============================================================================ +// Address Types +// ============================================================================ + +// Address on the blockchain +type Address struct { + Address string + Network Network + PubkeyHash []byte +} + +// ============================================================================ +// Hash Types +// ============================================================================ + +// Hash256 is a 256-bit hash +type Hash256 struct { + Bytes []byte + Hex string +} + +// NewHash256 creates a hash from bytes +func NewHash256(bytes []byte) Hash256 { + return Hash256{ + Bytes: bytes, + Hex: hex.EncodeToString(bytes), + } +} + +// ============================================================================ +// Negotiation Types +// ============================================================================ + +// AlgorithmCapabilities for negotiation +type AlgorithmCapabilities struct { + PqAlgorithms []PqAlgorithm + Classical bool + Hybrid bool + Preferred *PqAlgorithm +} + +// NegotiationPolicy for algorithm selection +type NegotiationPolicy struct { + MinSecurityLevel int + PreferCompact bool + AllowClassical bool + RequiredFamilies []AlgorithmFamily +} + +// NegotiationResult after negotiation +type NegotiationResult struct { + Algorithm PqAlgorithm + SecurityLevel int + Family AlgorithmFamily + Hybrid bool +} + +// SessionParams after establishing a session +type SessionParams struct { + Algorithm PqAlgorithm + SessionKey []byte + ExpiresAt *int64 +} + +// ============================================================================ +// Error Types +// ============================================================================ + +// CryptoError represents a crypto operation error +type CryptoError struct { + Message string + Code string +} + +func (e *CryptoError) Error() string { + if e.Code != "" { + return fmt.Sprintf("%s (code: %s)", e.Message, e.Code) + } + return e.Message +} + +// NewCryptoError creates a new error +func NewCryptoError(message, code string) *CryptoError { + return &CryptoError{Message: message, Code: code} +} + +// ============================================================================ +// Constants +// ============================================================================ + +const ( + Ed25519PublicKeySize = 32 + Ed25519SecretKeySize = 32 + Ed25519SignatureSize = 64 + Dilithium3PublicKeySize = 1952 + Dilithium3SignatureSize = 3293 + HybridSignatureSize = 64 + 3293 + Falcon512SignatureSize = 690 + Falcon512PublicKeySize = 897 + Falcon1024SignatureSize = 1330 + Falcon1024PublicKeySize = 1793 + Sphincs128sSignatureSize = 7856 + Sphincs192sSignatureSize = 16224 + Sphincs256sSignatureSize = 29792 + MinPBKDF2Iterations = 10000 + MinSaltLength = 8 + DefaultEndpoint = "https://crypto.synor.io/v1" +) + +// AlgorithmSizes maps algorithms to their sizes +var AlgorithmSizes = map[string]struct { + Signature int + PublicKey int +}{ + "ed25519": {64, 32}, + "dilithium3": {3293, 1952}, + "hybrid": {3357, 1984}, + "falcon512": {690, 897}, + "falcon1024": {1330, 1793}, + "sphincs128s": {7856, 32}, + "sphincs192s": {16224, 48}, + "sphincs256s": {29792, 64}, +} + +// ============================================================================ +// Helper Functions +// ============================================================================ + +// EncodeBase64 encodes bytes to base64 +func EncodeBase64(data []byte) string { + return base64.StdEncoding.EncodeToString(data) +} + +// DecodeBase64 decodes base64 to bytes +func DecodeBase64(s string) ([]byte, error) { + return base64.StdEncoding.DecodeString(s) +} + +// FalconSignatureSize returns the signature size for a variant +func FalconSignatureSize(variant FalconVariant) int { + switch variant { + case Falcon512: + return Falcon512SignatureSize + case Falcon1024: + return Falcon1024SignatureSize + default: + return 0 + } +} + +// FalconPublicKeySize returns the public key size for a variant +func FalconPublicKeySize(variant FalconVariant) int { + switch variant { + case Falcon512: + return Falcon512PublicKeySize + case Falcon1024: + return Falcon1024PublicKeySize + default: + return 0 + } +} + +// FalconSecurityLevel returns the security level for a variant +func FalconSecurityLevel(variant FalconVariant) int { + switch variant { + case Falcon512: + return 128 + case Falcon1024: + return 256 + default: + return 0 + } +} + +// SphincsSignatureSize returns the signature size for a variant +func SphincsSignatureSize(variant SphincsVariant) int { + switch variant { + case Shake128s: + return Sphincs128sSignatureSize + case Shake192s: + return Sphincs192sSignatureSize + case Shake256s: + return Sphincs256sSignatureSize + default: + return 0 + } +} + +// SphincsSecurityLevel returns the security level for a variant +func SphincsSecurityLevel(variant SphincsVariant) int { + switch variant { + case Shake128s: + return 128 + case Shake192s: + return 192 + case Shake256s: + return 256 + default: + return 0 + } +} diff --git a/sdk/java/src/main/java/io/synor/crypto/SynorCrypto.java b/sdk/java/src/main/java/io/synor/crypto/SynorCrypto.java new file mode 100644 index 0000000..63bc64d --- /dev/null +++ b/sdk/java/src/main/java/io/synor/crypto/SynorCrypto.java @@ -0,0 +1,351 @@ +package io.synor.crypto; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Synor Crypto SDK for Java + * + * Quantum-resistant cryptographic primitives for the Synor blockchain. + * + *
{@code
+ * CryptoConfig config = new CryptoConfig("your-api-key");
+ * SynorCrypto crypto = new SynorCrypto(config);
+ *
+ * // Generate a mnemonic
+ * Mnemonic mnemonic = crypto.mnemonic().generate(24).join();
+ * System.out.println("Backup words: " + mnemonic.getPhrase());
+ *
+ * // Create keypair from mnemonic
+ * HybridKeypair keypair = crypto.keypairs().fromMnemonic(mnemonic.getPhrase(), "").join();
+ * String address = keypair.getAddress(Network.MAINNET);
+ *
+ * // Sign a message
+ * HybridSignature signature = crypto.signing().sign(keypair, "Hello!".getBytes()).join();
+ * }
+ */ +public class SynorCrypto { + private final CryptoConfig config; + private final HttpClient httpClient; + private final Gson gson; + private final AtomicBoolean closed = new AtomicBoolean(false); + + private final MnemonicClient mnemonicClient; + private final KeypairClient keypairClient; + private final SigningClient signingClient; + private final FalconClient falconClient; + private final SphincsClient sphincsClient; + private final KdfClient kdfClient; + private final HashClient hashClient; + + public SynorCrypto(CryptoConfig config) { + this.config = config; + this.httpClient = HttpClient.newBuilder() + .connectTimeout(Duration.ofMillis(config.getTimeout())) + .build(); + this.gson = new GsonBuilder() + .setFieldNamingPolicy(com.google.gson.FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .create(); + + this.mnemonicClient = new MnemonicClient(this); + this.keypairClient = new KeypairClient(this); + this.signingClient = new SigningClient(this); + this.falconClient = new FalconClient(this); + this.sphincsClient = new SphincsClient(this); + this.kdfClient = new KdfClient(this); + this.hashClient = new HashClient(this); + } + + public Network getDefaultNetwork() { + return config.getDefaultNetwork(); + } + + public MnemonicClient mnemonic() { return mnemonicClient; } + public KeypairClient keypairs() { return keypairClient; } + public SigningClient signing() { return signingClient; } + public FalconClient falcon() { return falconClient; } + public SphincsClient sphincs() { return sphincsClient; } + public KdfClient kdf() { return kdfClient; } + public HashClient hash() { return hashClient; } + + public CompletableFuture healthCheck() { + return get("/health", new TypeToken>() {}) + .thenApply(result -> "healthy".equals(result.get("status"))) + .exceptionally(e -> false); + } + + public CompletableFuture> getInfo() { + return get("/info", new TypeToken>() {}); + } + + public void close() { + closed.set(true); + } + + public boolean isClosed() { + return closed.get(); + } + + CompletableFuture get(String path, TypeToken type) { + checkClosed(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(config.getEndpoint() + path)) + .header("Authorization", "Bearer " + config.getApiKey()) + .header("X-SDK-Version", "java/0.1.0") + .GET() + .build(); + + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(response -> handleResponse(response, type)); + } + + CompletableFuture post(String path, Object body, TypeToken type) { + checkClosed(); + String jsonBody = body != null ? gson.toJson(body) : "{}"; + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(config.getEndpoint() + path)) + .header("Authorization", "Bearer " + config.getApiKey()) + .header("Content-Type", "application/json") + .header("X-SDK-Version", "java/0.1.0") + .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) + .build(); + + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(response -> handleResponse(response, type)); + } + + private T handleResponse(HttpResponse response, TypeToken type) { + if (response.statusCode() >= 400) { + Map error = gson.fromJson(response.body(), new TypeToken>() {}.getType()); + String message = error != null && error.get("message") != null + ? (String) error.get("message") + : "HTTP " + response.statusCode(); + String code = error != null ? (String) error.get("code") : null; + throw new CryptoException(message, code); + } + return gson.fromJson(response.body(), type.getType()); + } + + private void checkClosed() { + if (closed.get()) { + throw new CryptoException("Client has been closed", "CLIENT_CLOSED"); + } + } + + // ======================================================================== + // Sub-Clients + // ======================================================================== + + public static class MnemonicClient { + private final SynorCrypto crypto; + MnemonicClient(SynorCrypto crypto) { this.crypto = crypto; } + + public CompletableFuture generate(int wordCount) { + Map body = Map.of("word_count", wordCount); + return crypto.post("/mnemonic/generate", body, new TypeToken() {}); + } + + public CompletableFuture fromPhrase(String phrase) { + Map body = Map.of("phrase", phrase); + return crypto.post("/mnemonic/from-phrase", body, new TypeToken() {}); + } + + public CompletableFuture validate(String phrase) { + Map body = Map.of("phrase", phrase); + return crypto.post("/mnemonic/validate", body, new TypeToken() {}); + } + + public CompletableFuture toSeed(String phrase, String passphrase) { + Map body = Map.of("phrase", phrase, "passphrase", passphrase); + return crypto.post("/mnemonic/to-seed", body, new TypeToken>() {}) + .thenApply(result -> Base64.getDecoder().decode(result.get("seed"))); + } + + public CompletableFuture> suggestWords(String partial, int limit) { + Map body = Map.of("partial", partial, "limit", limit); + return crypto.post("/mnemonic/suggest", body, new TypeToken>>() {}) + .thenApply(result -> result.get("suggestions")); + } + } + + public static class KeypairClient { + private final SynorCrypto crypto; + KeypairClient(SynorCrypto crypto) { this.crypto = crypto; } + + public CompletableFuture generate() { + return crypto.post("/keypair/generate", null, new TypeToken() {}); + } + + public CompletableFuture fromMnemonic(String phrase, String passphrase) { + Map body = Map.of("phrase", phrase, "passphrase", passphrase); + return crypto.post("/keypair/from-mnemonic", body, new TypeToken() {}); + } + + public CompletableFuture fromSeed(byte[] seed) { + Map body = Map.of("seed", Base64.getEncoder().encodeToString(seed)); + return crypto.post("/keypair/from-seed", body, new TypeToken() {}); + } + + public CompletableFuture
getAddress(HybridPublicKey publicKey, Network network) { + Map body = Map.of( + "public_key", publicKey.toMap(), + "network", network.getValue() + ); + return crypto.post("/keypair/address", body, new TypeToken
() {}); + } + } + + public static class SigningClient { + private final SynorCrypto crypto; + SigningClient(SynorCrypto crypto) { this.crypto = crypto; } + + public CompletableFuture sign(HybridKeypair keypair, byte[] message) { + Map body = Map.of( + "secret_key", keypair.getSecretKey().toMap(), + "message", Base64.getEncoder().encodeToString(message) + ); + return crypto.post("/sign/hybrid", body, new TypeToken() {}); + } + + public CompletableFuture verify(HybridPublicKey publicKey, byte[] message, HybridSignature signature) { + Map body = Map.of( + "public_key", publicKey.toMap(), + "message", Base64.getEncoder().encodeToString(message), + "signature", signature.toMap() + ); + return crypto.post("/sign/verify", body, new TypeToken>() {}) + .thenApply(result -> result.get("valid")); + } + + public CompletableFuture signEd25519(byte[] secretKey, byte[] message) { + Map body = Map.of( + "secret_key", Base64.getEncoder().encodeToString(secretKey), + "message", Base64.getEncoder().encodeToString(message) + ); + return crypto.post("/sign/ed25519", body, new TypeToken>() {}) + .thenApply(result -> Base64.getDecoder().decode(result.get("signature"))); + } + } + + public static class FalconClient { + private final SynorCrypto crypto; + FalconClient(SynorCrypto crypto) { this.crypto = crypto; } + + public CompletableFuture generate(FalconVariant variant) { + Map body = Map.of("variant", variant.getValue()); + return crypto.post("/falcon/generate", body, new TypeToken() {}); + } + + public CompletableFuture sign(FalconKeypair keypair, byte[] message) { + Map body = Map.of( + "variant", keypair.getVariant().getValue(), + "secret_key", Base64.getEncoder().encodeToString(keypair.getSecretKey().getBytes()), + "message", Base64.getEncoder().encodeToString(message) + ); + return crypto.post("/falcon/sign", body, new TypeToken() {}); + } + + public CompletableFuture verify(byte[] publicKey, byte[] message, FalconSignature signature) { + Map body = Map.of( + "variant", signature.getVariant().getValue(), + "public_key", Base64.getEncoder().encodeToString(publicKey), + "message", Base64.getEncoder().encodeToString(message), + "signature", Base64.getEncoder().encodeToString(signature.getBytes()) + ); + return crypto.post("/falcon/verify", body, new TypeToken>() {}) + .thenApply(result -> result.get("valid")); + } + } + + public static class SphincsClient { + private final SynorCrypto crypto; + SphincsClient(SynorCrypto crypto) { this.crypto = crypto; } + + public CompletableFuture generate(SphincsVariant variant) { + Map body = Map.of("variant", variant.getValue()); + return crypto.post("/sphincs/generate", body, new TypeToken() {}); + } + + public CompletableFuture sign(SphincsKeypair keypair, byte[] message) { + Map body = Map.of( + "variant", keypair.getVariant().getValue(), + "secret_key", Base64.getEncoder().encodeToString(keypair.getSecretKey().getBytes()), + "message", Base64.getEncoder().encodeToString(message) + ); + return crypto.post("/sphincs/sign", body, new TypeToken() {}); + } + + public CompletableFuture verify(byte[] publicKey, byte[] message, SphincsSignature signature) { + Map body = Map.of( + "variant", signature.getVariant().getValue(), + "public_key", Base64.getEncoder().encodeToString(publicKey), + "message", Base64.getEncoder().encodeToString(message), + "signature", Base64.getEncoder().encodeToString(signature.getBytes()) + ); + return crypto.post("/sphincs/verify", body, new TypeToken>() {}) + .thenApply(result -> result.get("valid")); + } + } + + public static class KdfClient { + private final SynorCrypto crypto; + KdfClient(SynorCrypto crypto) { this.crypto = crypto; } + + public CompletableFuture deriveKey(byte[] seed, DerivationConfig config) { + Map body = new HashMap<>(); + body.put("seed", Base64.getEncoder().encodeToString(seed)); + body.put("output_length", config.getOutputLength()); + if (config.getSalt() != null) { + body.put("salt", Base64.getEncoder().encodeToString(config.getSalt())); + } + if (config.getInfo() != null) { + body.put("info", Base64.getEncoder().encodeToString(config.getInfo())); + } + return crypto.post("/kdf/hkdf", body, new TypeToken>() {}) + .thenApply(result -> Base64.getDecoder().decode(result.get("key"))); + } + + public CompletableFuture deriveFromPassword(byte[] password, PasswordDerivationConfig config) { + Map body = Map.of( + "password", Base64.getEncoder().encodeToString(password), + "salt", Base64.getEncoder().encodeToString(config.getSalt()), + "iterations", config.getIterations(), + "output_length", config.getOutputLength() + ); + return crypto.post("/kdf/pbkdf2", body, new TypeToken>() {}) + .thenApply(result -> Base64.getDecoder().decode(result.get("key"))); + } + } + + public static class HashClient { + private final SynorCrypto crypto; + HashClient(SynorCrypto crypto) { this.crypto = crypto; } + + public CompletableFuture sha3_256(byte[] data) { + Map body = Map.of("data", Base64.getEncoder().encodeToString(data)); + return crypto.post("/hash/sha3-256", body, new TypeToken() {}); + } + + public CompletableFuture blake3(byte[] data) { + Map body = Map.of("data", Base64.getEncoder().encodeToString(data)); + return crypto.post("/hash/blake3", body, new TypeToken() {}); + } + + public CompletableFuture keccak256(byte[] data) { + Map body = Map.of("data", Base64.getEncoder().encodeToString(data)); + return crypto.post("/hash/keccak256", body, new TypeToken() {}); + } + } +} diff --git a/sdk/java/src/main/java/io/synor/crypto/Types.java b/sdk/java/src/main/java/io/synor/crypto/Types.java new file mode 100644 index 0000000..309543f --- /dev/null +++ b/sdk/java/src/main/java/io/synor/crypto/Types.java @@ -0,0 +1,404 @@ +package io.synor.crypto; + +import java.util.*; + +/** + * Synor Crypto SDK Types for Java + */ + +// ============================================================================ +// Enumerations +// ============================================================================ + +enum Network { + MAINNET("mainnet"), + TESTNET("testnet"), + DEVNET("devnet"); + + private final String value; + Network(String value) { this.value = value; } + public String getValue() { return value; } + public static Network fromValue(String value) { + for (Network n : values()) if (n.value.equals(value)) return n; + return MAINNET; + } +} + +enum FalconVariant { + FALCON512("falcon512", 690, 897, 128), + FALCON1024("falcon1024", 1330, 1793, 256); + + private final String value; + private final int signatureSize; + private final int publicKeySize; + private final int securityLevel; + + FalconVariant(String value, int sigSize, int pkSize, int secLevel) { + this.value = value; + this.signatureSize = sigSize; + this.publicKeySize = pkSize; + this.securityLevel = secLevel; + } + + public String getValue() { return value; } + public int getSignatureSize() { return signatureSize; } + public int getPublicKeySize() { return publicKeySize; } + public int getSecurityLevel() { return securityLevel; } +} + +enum SphincsVariant { + SHAKE128S("shake128s", 7856, 128), + SHAKE192S("shake192s", 16224, 192), + SHAKE256S("shake256s", 29792, 256); + + private final String value; + private final int signatureSize; + private final int securityLevel; + + SphincsVariant(String value, int sigSize, int secLevel) { + this.value = value; + this.signatureSize = sigSize; + this.securityLevel = secLevel; + } + + public String getValue() { return value; } + public int getSignatureSize() { return signatureSize; } + public int getSecurityLevel() { return securityLevel; } +} + +enum PqAlgorithm { + DILITHIUM3("dilithium3"), + FALCON512("falcon512"), + FALCON1024("falcon1024"), + SPHINCS128S("sphincs128s"), + SPHINCS192S("sphincs192s"), + SPHINCS256S("sphincs256s"); + + private final String value; + PqAlgorithm(String value) { this.value = value; } + public String getValue() { return value; } +} + +enum AlgorithmFamily { + CLASSICAL("classical"), + LATTICE("lattice"), + HASH_BASED("hash_based"), + HYBRID("hybrid"); + + private final String value; + AlgorithmFamily(String value) { this.value = value; } + public String getValue() { return value; } +} + +// ============================================================================ +// Configuration Types +// ============================================================================ + +class CryptoConfig { + private final String apiKey; + private String endpoint = "https://crypto.synor.io/v1"; + private int timeout = 30000; + private int retries = 3; + private boolean debug = false; + private Network defaultNetwork = Network.MAINNET; + + public CryptoConfig(String apiKey) { this.apiKey = apiKey; } + + public String getApiKey() { return apiKey; } + public String getEndpoint() { return endpoint; } + public int getTimeout() { return timeout; } + public int getRetries() { return retries; } + public boolean isDebug() { return debug; } + public Network getDefaultNetwork() { return defaultNetwork; } + + public CryptoConfig setEndpoint(String endpoint) { this.endpoint = endpoint; return this; } + public CryptoConfig setTimeout(int timeout) { this.timeout = timeout; return this; } + public CryptoConfig setRetries(int retries) { this.retries = retries; return this; } + public CryptoConfig setDebug(boolean debug) { this.debug = debug; return this; } + public CryptoConfig setDefaultNetwork(Network network) { this.defaultNetwork = network; return this; } +} + +class DerivationConfig { + private byte[] salt; + private byte[] info; + private int outputLength = 32; + + public byte[] getSalt() { return salt; } + public byte[] getInfo() { return info; } + public int getOutputLength() { return outputLength; } + + public DerivationConfig setSalt(byte[] salt) { this.salt = salt; return this; } + public DerivationConfig setInfo(byte[] info) { this.info = info; return this; } + public DerivationConfig setOutputLength(int len) { this.outputLength = len; return this; } +} + +class PasswordDerivationConfig { + private final byte[] salt; + private int iterations = 100000; + private int outputLength = 32; + + public PasswordDerivationConfig(byte[] salt) { this.salt = salt; } + + public byte[] getSalt() { return salt; } + public int getIterations() { return iterations; } + public int getOutputLength() { return outputLength; } + + public PasswordDerivationConfig setIterations(int iterations) { this.iterations = iterations; return this; } + public PasswordDerivationConfig setOutputLength(int len) { this.outputLength = len; return this; } +} + +class DerivationPath { + public static final int COIN_TYPE = 0x5359; + private final int account; + private final int change; + private final int index; + + public DerivationPath(int account, int change, int index) { + this.account = account; + this.change = change; + this.index = index; + } + + public static DerivationPath external(int account, int index) { return new DerivationPath(account, 0, index); } + public static DerivationPath internal(int account, int index) { return new DerivationPath(account, 1, index); } + + public int getAccount() { return account; } + public int getChange() { return change; } + public int getIndex() { return index; } + + @Override + public String toString() { return String.format("m/44'/%d'/%d'/%d/%d", COIN_TYPE, account, change, index); } + + public Map toMap() { return Map.of("account", account, "change", change, "index", index); } +} + +// ============================================================================ +// Key Types +// ============================================================================ + +class HybridPublicKey { + private String ed25519; + private String dilithium; + + public byte[] getEd25519() { return Base64.getDecoder().decode(ed25519); } + public byte[] getDilithium() { return Base64.getDecoder().decode(dilithium); } + public int size() { return getEd25519().length + getDilithium().length; } + + public Map toMap() { + return Map.of("ed25519", ed25519, "dilithium", dilithium); + } +} + +class SecretKey { + private String ed25519Seed; + private String masterSeed; + + public byte[] getEd25519Seed() { return Base64.getDecoder().decode(ed25519Seed); } + public byte[] getMasterSeed() { return Base64.getDecoder().decode(masterSeed); } + + public Map toMap() { + return Map.of("ed25519_seed", ed25519Seed, "master_seed", masterSeed); + } +} + +class FalconPublicKey { + private String variant; + private String bytes; + + public FalconVariant getVariant() { return FalconVariant.valueOf(variant.toUpperCase()); } + public byte[] getBytes() { return Base64.getDecoder().decode(bytes); } +} + +class FalconSecretKey { + private String variant; + private String bytes; + + public FalconVariant getVariant() { return FalconVariant.valueOf(variant.toUpperCase()); } + public byte[] getBytes() { return Base64.getDecoder().decode(bytes); } +} + +class SphincsPublicKey { + private String variant; + private String bytes; + + public SphincsVariant getVariant() { return SphincsVariant.valueOf(variant.toUpperCase()); } + public byte[] getBytes() { return Base64.getDecoder().decode(bytes); } +} + +class SphincsSecretKey { + private String variant; + private String bytes; + + public SphincsVariant getVariant() { return SphincsVariant.valueOf(variant.toUpperCase()); } + public byte[] getBytes() { return Base64.getDecoder().decode(bytes); } +} + +// ============================================================================ +// Signature Types +// ============================================================================ + +class HybridSignature { + private String ed25519; + private String dilithium; + + public byte[] getEd25519() { return Base64.getDecoder().decode(ed25519); } + public byte[] getDilithium() { return Base64.getDecoder().decode(dilithium); } + public int size() { return getEd25519().length + getDilithium().length; } + + public byte[] toBytes() { + byte[] e = getEd25519(); + byte[] d = getDilithium(); + byte[] result = new byte[e.length + d.length]; + System.arraycopy(e, 0, result, 0, e.length); + System.arraycopy(d, 0, result, e.length, d.length); + return result; + } + + public Map toMap() { + return Map.of("ed25519", ed25519, "dilithium", dilithium); + } +} + +class FalconSignature { + private String variant; + private String signature; + + public FalconVariant getVariant() { return FalconVariant.valueOf(variant.toUpperCase()); } + public byte[] getBytes() { return Base64.getDecoder().decode(signature); } + public int size() { return getBytes().length; } +} + +class SphincsSignature { + private String variant; + private String signature; + + public SphincsVariant getVariant() { return SphincsVariant.valueOf(variant.toUpperCase()); } + public byte[] getBytes() { return Base64.getDecoder().decode(signature); } + public int size() { return getBytes().length; } +} + +// ============================================================================ +// Keypair Types +// ============================================================================ + +class HybridKeypair { + private HybridPublicKey publicKey; + private SecretKey secretKey; + private Map addresses = new HashMap<>(); + + public HybridPublicKey getPublicKey() { return publicKey; } + public SecretKey getSecretKey() { return secretKey; } + + public String getAddress(Network network) { + return addresses.getOrDefault(network.getValue(), ""); + } +} + +class FalconKeypair { + private String variant; + private FalconPublicKey publicKey; + private FalconSecretKey secretKey; + + public FalconVariant getVariant() { return FalconVariant.valueOf(variant.toUpperCase()); } + public FalconPublicKey getPublicKey() { return publicKey; } + public FalconSecretKey getSecretKey() { return secretKey; } +} + +class SphincsKeypair { + private String variant; + private SphincsPublicKey publicKey; + private SphincsSecretKey secretKey; + + public SphincsVariant getVariant() { return SphincsVariant.valueOf(variant.toUpperCase()); } + public SphincsPublicKey getPublicKey() { return publicKey; } + public SphincsSecretKey getSecretKey() { return secretKey; } +} + +// ============================================================================ +// Mnemonic Types +// ============================================================================ + +class Mnemonic { + private String phrase; + private List words; + private int wordCount; + private String entropy; + + public String getPhrase() { return phrase; } + public List getWords() { return words != null ? words : Arrays.asList(phrase.split(" ")); } + public int getWordCount() { return wordCount > 0 ? wordCount : getWords().size(); } + public byte[] getEntropy() { return entropy != null ? Base64.getDecoder().decode(entropy) : new byte[0]; } +} + +class MnemonicValidation { + private boolean valid; + private String error; + + public boolean isValid() { return valid; } + public String getError() { return error; } +} + +// ============================================================================ +// Address Types +// ============================================================================ + +class Address { + private String address; + private String network; + private String pubkeyHash; + + public String getAddress() { return address; } + public Network getNetwork() { return Network.fromValue(network); } + public byte[] getPubkeyHash() { return pubkeyHash != null ? Base64.getDecoder().decode(pubkeyHash) : new byte[0]; } +} + +// ============================================================================ +// Hash Types +// ============================================================================ + +class Hash256 { + private String hash; + + public String getHex() { return hash; } + public byte[] getBytes() { + byte[] result = new byte[hash.length() / 2]; + for (int i = 0; i < result.length; i++) { + result[i] = (byte) Integer.parseInt(hash.substring(i * 2, i * 2 + 2), 16); + } + return result; + } +} + +// ============================================================================ +// Error Types +// ============================================================================ + +class CryptoException extends RuntimeException { + private final String code; + + public CryptoException(String message, String code) { + super(message); + this.code = code; + } + + public String getCode() { return code; } +} + +// ============================================================================ +// Constants +// ============================================================================ + +final class CryptoConstants { + public static final int ED25519_PUBLIC_KEY_SIZE = 32; + public static final int ED25519_SECRET_KEY_SIZE = 32; + public static final int ED25519_SIGNATURE_SIZE = 64; + public static final int DILITHIUM3_PUBLIC_KEY_SIZE = 1952; + public static final int DILITHIUM3_SIGNATURE_SIZE = 3293; + public static final int HYBRID_SIGNATURE_SIZE = 64 + 3293; + public static final int COIN_TYPE = 0x5359; + public static final int MIN_PBKDF2_ITERATIONS = 10000; + public static final int MIN_SALT_LENGTH = 8; + public static final String DEFAULT_ENDPOINT = "https://crypto.synor.io/v1"; + + private CryptoConstants() {} +} diff --git a/sdk/js/src/crypto/index.ts b/sdk/js/src/crypto/index.ts new file mode 100644 index 0000000..151468d --- /dev/null +++ b/sdk/js/src/crypto/index.ts @@ -0,0 +1,767 @@ +/** + * Synor Crypto SDK for JavaScript/TypeScript + * + * Quantum-resistant cryptographic primitives for the Synor blockchain. + * + * Features: + * - Hybrid Ed25519 + Dilithium3 signatures (quantum-resistant) + * - BIP-39 mnemonic support (12-24 words) + * - BIP-44 hierarchical key derivation + * - Post-quantum algorithms: Falcon, SPHINCS+ + * - Secure key derivation (HKDF, PBKDF2) + * + * @example + * ```typescript + * import { SynorCrypto } from '@synor/crypto'; + * + * const crypto = new SynorCrypto({ apiKey: 'your-api-key' }); + * + * // Generate a mnemonic + * const mnemonic = await crypto.mnemonic.generate(24); + * console.log('Backup words:', mnemonic.phrase); + * + * // Create keypair from mnemonic + * const keypair = await crypto.keypairs.fromMnemonic(mnemonic); + * const address = keypair.address('mainnet'); + * + * // Sign a message + * const signature = await crypto.signing.sign(keypair, Buffer.from('Hello!')); + * ``` + */ + +import type { + CryptoConfig, + Network, + MnemonicWordCount, + Mnemonic, + MnemonicValidation, + HybridKeypair, + HybridPublicKey, + HybridSignature, + FalconVariant, + FalconKeypair, + FalconSignature, + SphincsVariant, + SphincsKeypair, + SphincsSignature, + DerivationPath, + DerivationConfig, + PasswordDerivationConfig, + AlgorithmCapabilities, + NegotiationPolicy, + NegotiationResult, + SessionParams, + Hash256, + Address, +} from './types'; + +export * from './types'; + +// ============================================================================ +// Mnemonic Client +// ============================================================================ + +/** Mnemonic operations */ +export class MnemonicClient { + constructor(private crypto: SynorCrypto) {} + + /** + * Generates a new random mnemonic. + * @param wordCount Number of words (12, 15, 18, 21, or 24) + */ + async generate(wordCount: MnemonicWordCount = 24): Promise { + const body = { word_count: wordCount }; + return this.crypto.post('/mnemonic/generate', body); + } + + /** + * Creates a mnemonic from a phrase string. + */ + async fromPhrase(phrase: string): Promise { + return this.crypto.post('/mnemonic/from-phrase', { phrase }); + } + + /** + * Creates a mnemonic from entropy bytes. + */ + async fromEntropy(entropy: Uint8Array): Promise { + const entropyBase64 = Buffer.from(entropy).toString('base64'); + return this.crypto.post('/mnemonic/from-entropy', { entropy: entropyBase64 }); + } + + /** + * Validates a mnemonic phrase. + */ + async validate(phrase: string): Promise { + return this.crypto.post('/mnemonic/validate', { phrase }); + } + + /** + * Derives a 64-byte seed from a mnemonic. + * @param mnemonic The mnemonic phrase or object + * @param passphrase Optional BIP-39 passphrase + */ + async toSeed(mnemonic: Mnemonic | string, passphrase: string = ''): Promise { + const phrase = typeof mnemonic === 'string' ? mnemonic : mnemonic.phrase; + const result = await this.crypto.post<{ seed: string }>('/mnemonic/to-seed', { + phrase, + passphrase, + }); + return Buffer.from(result.seed, 'base64'); + } + + /** + * Suggests word completions from the BIP-39 wordlist. + */ + async suggestWords(partial: string, limit: number = 10): Promise { + const result = await this.crypto.post<{ suggestions: string[] }>('/mnemonic/suggest', { + partial, + limit, + }); + return result.suggestions; + } +} + +// ============================================================================ +// Keypair Client +// ============================================================================ + +/** Keypair operations */ +export class KeypairClient { + constructor(private crypto: SynorCrypto) {} + + /** + * Generates a new random hybrid keypair. + */ + async generate(): Promise { + const result = await this.crypto.post('/keypair/generate'); + return this.wrapKeypair(result); + } + + /** + * Creates a keypair from a mnemonic. + * @param mnemonic The mnemonic phrase or object + * @param passphrase Optional BIP-39 passphrase + */ + async fromMnemonic(mnemonic: Mnemonic | string, passphrase: string = ''): Promise { + const phrase = typeof mnemonic === 'string' ? mnemonic : mnemonic.phrase; + const result = await this.crypto.post('/keypair/from-mnemonic', { + phrase, + passphrase, + }); + return this.wrapKeypair(result); + } + + /** + * Creates a keypair from a 64-byte seed. + */ + async fromSeed(seed: Uint8Array): Promise { + const seedBase64 = Buffer.from(seed).toString('base64'); + const result = await this.crypto.post('/keypair/from-seed', { + seed: seedBase64, + }); + return this.wrapKeypair(result); + } + + /** + * Derives a child keypair using BIP-44 path. + */ + async derive( + parentKeypair: HybridKeypair, + path: DerivationPath + ): Promise { + const result = await this.crypto.post('/keypair/derive', { + public_key: this.encodePublicKey(parentKeypair.publicKey), + path, + }); + return this.wrapKeypair(result); + } + + /** + * Gets the address for a public key. + */ + async getAddress(publicKey: HybridPublicKey, network: Network): Promise
{ + return this.crypto.post
('/keypair/address', { + public_key: this.encodePublicKey(publicKey), + network, + }); + } + + private wrapKeypair(response: HybridKeypairResponse): HybridKeypair { + const publicKey: HybridPublicKey = { + ed25519: Buffer.from(response.public_key.ed25519, 'base64'), + dilithium: Buffer.from(response.public_key.dilithium, 'base64'), + }; + + return { + publicKey, + secretKey: { + ed25519Seed: Buffer.from(response.secret_key.ed25519_seed, 'base64'), + masterSeed: Buffer.from(response.secret_key.master_seed, 'base64'), + }, + address: (network: Network) => response.addresses[network] || '', + }; + } + + private encodePublicKey(pk: HybridPublicKey): object { + return { + ed25519: Buffer.from(pk.ed25519).toString('base64'), + dilithium: Buffer.from(pk.dilithium).toString('base64'), + }; + } +} + +interface HybridKeypairResponse { + public_key: { + ed25519: string; + dilithium: string; + }; + secret_key: { + ed25519_seed: string; + master_seed: string; + }; + addresses: Record; +} + +// ============================================================================ +// Signing Client +// ============================================================================ + +/** Signing and verification operations */ +export class SigningClient { + constructor(private crypto: SynorCrypto) {} + + /** + * Signs a message with a hybrid keypair. + */ + async sign(keypair: HybridKeypair, message: Uint8Array): Promise { + const result = await this.crypto.post<{ ed25519: string; dilithium: string }>( + '/sign/hybrid', + { + secret_key: { + ed25519_seed: Buffer.from(keypair.secretKey.ed25519Seed).toString('base64'), + master_seed: Buffer.from(keypair.secretKey.masterSeed).toString('base64'), + }, + message: Buffer.from(message).toString('base64'), + } + ); + + return { + ed25519: Buffer.from(result.ed25519, 'base64'), + dilithium: Buffer.from(result.dilithium, 'base64'), + }; + } + + /** + * Verifies a hybrid signature. + */ + async verify( + publicKey: HybridPublicKey, + message: Uint8Array, + signature: HybridSignature + ): Promise { + const result = await this.crypto.post<{ valid: boolean }>('/sign/verify', { + public_key: { + ed25519: Buffer.from(publicKey.ed25519).toString('base64'), + dilithium: Buffer.from(publicKey.dilithium).toString('base64'), + }, + message: Buffer.from(message).toString('base64'), + signature: { + ed25519: Buffer.from(signature.ed25519).toString('base64'), + dilithium: Buffer.from(signature.dilithium).toString('base64'), + }, + }); + return result.valid; + } + + /** + * Signs a message using Ed25519 only (faster but not quantum-resistant). + */ + async signEd25519(secretKey: Uint8Array, message: Uint8Array): Promise { + const result = await this.crypto.post<{ signature: string }>('/sign/ed25519', { + secret_key: Buffer.from(secretKey).toString('base64'), + message: Buffer.from(message).toString('base64'), + }); + return Buffer.from(result.signature, 'base64'); + } + + /** + * Verifies an Ed25519 signature. + */ + async verifyEd25519( + publicKey: Uint8Array, + message: Uint8Array, + signature: Uint8Array + ): Promise { + const result = await this.crypto.post<{ valid: boolean }>('/sign/verify-ed25519', { + public_key: Buffer.from(publicKey).toString('base64'), + message: Buffer.from(message).toString('base64'), + signature: Buffer.from(signature).toString('base64'), + }); + return result.valid; + } +} + +// ============================================================================ +// Falcon Client +// ============================================================================ + +/** Falcon (FIPS 206) compact signature operations */ +export class FalconClient { + constructor(private crypto: SynorCrypto) {} + + /** + * Generates a Falcon keypair. + * @param variant Falcon-512 (128-bit) or Falcon-1024 (256-bit) + */ + async generate(variant: FalconVariant = 'falcon512'): Promise { + return this.crypto.post('/falcon/generate', { variant }); + } + + /** + * Signs a message with Falcon. + */ + async sign(keypair: FalconKeypair, message: Uint8Array): Promise { + const result = await this.crypto.post<{ signature: string; variant: FalconVariant }>( + '/falcon/sign', + { + variant: keypair.variant, + secret_key: Buffer.from(keypair.secretKey.bytes).toString('base64'), + message: Buffer.from(message).toString('base64'), + } + ); + return { + variant: result.variant, + bytes: Buffer.from(result.signature, 'base64'), + }; + } + + /** + * Verifies a Falcon signature. + */ + async verify( + publicKey: Uint8Array, + message: Uint8Array, + signature: FalconSignature + ): Promise { + const result = await this.crypto.post<{ valid: boolean }>('/falcon/verify', { + variant: signature.variant, + public_key: Buffer.from(publicKey).toString('base64'), + message: Buffer.from(message).toString('base64'), + signature: Buffer.from(signature.bytes).toString('base64'), + }); + return result.valid; + } + + /** + * Returns the signature size for a variant. + */ + signatureSize(variant: FalconVariant): number { + return variant === 'falcon512' ? 690 : 1330; + } + + /** + * Returns the public key size for a variant. + */ + publicKeySize(variant: FalconVariant): number { + return variant === 'falcon512' ? 897 : 1793; + } + + /** + * Returns the security level in bits. + */ + securityLevel(variant: FalconVariant): number { + return variant === 'falcon512' ? 128 : 256; + } +} + +// ============================================================================ +// SPHINCS+ Client +// ============================================================================ + +/** SPHINCS+ (FIPS 205) hash-based signature operations */ +export class SphincsClient { + constructor(private crypto: SynorCrypto) {} + + /** + * Generates a SPHINCS+ keypair. + */ + async generate(variant: SphincsVariant = 'shake128s'): Promise { + return this.crypto.post('/sphincs/generate', { variant }); + } + + /** + * Signs a message with SPHINCS+. + */ + async sign(keypair: SphincsKeypair, message: Uint8Array): Promise { + const result = await this.crypto.post<{ signature: string; variant: SphincsVariant }>( + '/sphincs/sign', + { + variant: keypair.variant, + secret_key: Buffer.from(keypair.secretKey.bytes).toString('base64'), + message: Buffer.from(message).toString('base64'), + } + ); + return { + variant: result.variant, + bytes: Buffer.from(result.signature, 'base64'), + }; + } + + /** + * Verifies a SPHINCS+ signature. + */ + async verify( + publicKey: Uint8Array, + message: Uint8Array, + signature: SphincsSignature + ): Promise { + const result = await this.crypto.post<{ valid: boolean }>('/sphincs/verify', { + variant: signature.variant, + public_key: Buffer.from(publicKey).toString('base64'), + message: Buffer.from(message).toString('base64'), + signature: Buffer.from(signature.bytes).toString('base64'), + }); + return result.valid; + } + + /** + * Returns the signature size for a variant. + */ + signatureSize(variant: SphincsVariant): number { + const sizes: Record = { + shake128s: 7856, + shake192s: 16224, + shake256s: 29792, + }; + return sizes[variant]; + } + + /** + * Returns the security level in bits. + */ + securityLevel(variant: SphincsVariant): number { + const levels: Record = { + shake128s: 128, + shake192s: 192, + shake256s: 256, + }; + return levels[variant]; + } +} + +// ============================================================================ +// Key Derivation Client +// ============================================================================ + +/** Key derivation operations */ +export class KdfClient { + constructor(private crypto: SynorCrypto) {} + + /** + * Derives a key using HKDF-SHA3-256. + */ + async deriveKey( + seed: Uint8Array, + config: DerivationConfig = {} + ): Promise { + const result = await this.crypto.post<{ key: string }>('/kdf/hkdf', { + seed: Buffer.from(seed).toString('base64'), + salt: config.salt ? Buffer.from(config.salt).toString('base64') : undefined, + info: config.info ? Buffer.from(config.info).toString('base64') : undefined, + output_length: config.outputLength ?? 32, + }); + return Buffer.from(result.key, 'base64'); + } + + /** + * Derives a key from a password using PBKDF2. + */ + async deriveFromPassword( + password: string | Uint8Array, + config: PasswordDerivationConfig + ): Promise { + const passwordBytes = typeof password === 'string' + ? Buffer.from(password, 'utf-8') + : password; + + const result = await this.crypto.post<{ key: string }>('/kdf/pbkdf2', { + password: Buffer.from(passwordBytes).toString('base64'), + salt: Buffer.from(config.salt).toString('base64'), + iterations: config.iterations, + output_length: config.outputLength ?? 32, + }); + return Buffer.from(result.key, 'base64'); + } + + /** + * Derives an Ed25519 key from a master seed. + */ + async deriveEd25519Key(masterSeed: Uint8Array, account: number): Promise { + const result = await this.crypto.post<{ key: string }>('/kdf/ed25519', { + master_seed: Buffer.from(masterSeed).toString('base64'), + account, + }); + return Buffer.from(result.key, 'base64'); + } + + /** + * Derives a child key using BIP-32 style derivation. + */ + async deriveChildKey( + parentKey: Uint8Array, + chainCode: Uint8Array, + index: number + ): Promise<{ key: Uint8Array; chainCode: Uint8Array }> { + const result = await this.crypto.post<{ key: string; chain_code: string }>('/kdf/child', { + parent_key: Buffer.from(parentKey).toString('base64'), + chain_code: Buffer.from(chainCode).toString('base64'), + index, + }); + return { + key: Buffer.from(result.key, 'base64'), + chainCode: Buffer.from(result.chain_code, 'base64'), + }; + } + + /** + * Creates a BIP-44 derivation path. + */ + createPath(account: number, change: number = 0, index: number = 0): DerivationPath { + return { account, change, index }; + } + + /** + * Formats a derivation path as a string. + */ + formatPath(path: DerivationPath): string { + const coinType = 0x5359; // Synor coin type + return `m/44'/${coinType}'/${path.account}'/${path.change}/${path.index}`; + } +} + +// ============================================================================ +// Hashing Client +// ============================================================================ + +/** Hashing operations */ +export class HashClient { + constructor(private crypto: SynorCrypto) {} + + /** + * Computes SHA3-256 hash. + */ + async sha3_256(data: Uint8Array): Promise { + const result = await this.crypto.post<{ hash: string }>('/hash/sha3-256', { + data: Buffer.from(data).toString('base64'), + }); + const bytes = Buffer.from(result.hash, 'hex'); + return { bytes, hex: result.hash }; + } + + /** + * Computes BLAKE3 hash. + */ + async blake3(data: Uint8Array): Promise { + const result = await this.crypto.post<{ hash: string }>('/hash/blake3', { + data: Buffer.from(data).toString('base64'), + }); + const bytes = Buffer.from(result.hash, 'hex'); + return { bytes, hex: result.hash }; + } + + /** + * Computes Keccak-256 hash (Ethereum compatible). + */ + async keccak256(data: Uint8Array): Promise { + const result = await this.crypto.post<{ hash: string }>('/hash/keccak256', { + data: Buffer.from(data).toString('base64'), + }); + const bytes = Buffer.from(result.hash, 'hex'); + return { bytes, hex: result.hash }; + } + + /** + * Combines multiple hashes. + */ + async combine(hashes: Uint8Array[]): Promise { + const result = await this.crypto.post<{ hash: string }>('/hash/combine', { + hashes: hashes.map(h => Buffer.from(h).toString('base64')), + }); + const bytes = Buffer.from(result.hash, 'hex'); + return { bytes, hex: result.hash }; + } +} + +// ============================================================================ +// Negotiation Client +// ============================================================================ + +/** Algorithm negotiation operations */ +export class NegotiationClient { + constructor(private crypto: SynorCrypto) {} + + /** + * Gets the local capabilities. + */ + async getCapabilities(): Promise { + return this.crypto.get('/negotiation/capabilities'); + } + + /** + * Negotiates algorithm selection with a peer. + */ + async negotiate( + peerCapabilities: AlgorithmCapabilities, + policy: NegotiationPolicy + ): Promise { + return this.crypto.post('/negotiation/negotiate', { + peer_capabilities: peerCapabilities, + policy, + }); + } + + /** + * Establishes session parameters after negotiation. + */ + async establishSession( + result: NegotiationResult, + peerPublicKey: Uint8Array + ): Promise { + return this.crypto.post('/negotiation/session', { + negotiation_result: result, + peer_public_key: Buffer.from(peerPublicKey).toString('base64'), + }); + } +} + +// ============================================================================ +// Main Client +// ============================================================================ + +/** + * Synor Crypto SDK Client + * + * Provides quantum-resistant cryptographic operations for the Synor blockchain. + */ +export class SynorCrypto { + private config: Required; + private closed = false; + + /** Mnemonic operations */ + readonly mnemonic: MnemonicClient; + /** Keypair operations */ + readonly keypairs: KeypairClient; + /** Signing operations */ + readonly signing: SigningClient; + /** Falcon (compact PQ) operations */ + readonly falcon: FalconClient; + /** SPHINCS+ (hash-based) operations */ + readonly sphincs: SphincsClient; + /** Key derivation operations */ + readonly kdf: KdfClient; + /** Hashing operations */ + readonly hash: HashClient; + /** Algorithm negotiation */ + readonly negotiation: NegotiationClient; + + constructor(config: CryptoConfig) { + this.config = { + apiKey: config.apiKey, + endpoint: config.endpoint ?? 'https://crypto.synor.io/v1', + timeout: config.timeout ?? 30000, + retries: config.retries ?? 3, + debug: config.debug ?? false, + defaultNetwork: config.defaultNetwork ?? 'mainnet', + }; + + this.mnemonic = new MnemonicClient(this); + this.keypairs = new KeypairClient(this); + this.signing = new SigningClient(this); + this.falcon = new FalconClient(this); + this.sphincs = new SphincsClient(this); + this.kdf = new KdfClient(this); + this.hash = new HashClient(this); + this.negotiation = new NegotiationClient(this); + } + + /** Default network */ + get defaultNetwork(): Network { + return this.config.defaultNetwork; + } + + /** Health check */ + async healthCheck(): Promise { + try { + const result = await this.get<{ status: string }>('/health'); + return result.status === 'healthy'; + } catch { + return false; + } + } + + /** Get service info */ + async getInfo(): Promise> { + return this.get>('/info'); + } + + /** Close the client */ + close(): void { + this.closed = true; + } + + /** Check if client is closed */ + get isClosed(): boolean { + return this.closed; + } + + /** @internal */ + async get(path: string): Promise { + this.checkClosed(); + const response = await fetch(`${this.config.endpoint}${path}`, { + method: 'GET', + headers: this.headers(), + }); + return this.handleResponse(response); + } + + /** @internal */ + async post(path: string, body?: unknown): Promise { + this.checkClosed(); + const response = await fetch(`${this.config.endpoint}${path}`, { + method: 'POST', + headers: this.headers(), + body: body ? JSON.stringify(body) : undefined, + }); + return this.handleResponse(response); + } + + private headers(): Record { + return { + 'Authorization': `Bearer ${this.config.apiKey}`, + 'Content-Type': 'application/json', + 'X-SDK-Version': 'js/0.1.0', + }; + } + + private async handleResponse(response: Response): Promise { + const json = await response.json(); + if (!response.ok) { + const { CryptoError } = await import('./types'); + throw new CryptoError( + json.message ?? `HTTP ${response.status}`, + json.code + ); + } + return json as T; + } + + private checkClosed(): void { + if (this.closed) { + const { CryptoError } = require('./types'); + throw new CryptoError('Client has been closed', 'NETWORK_ERROR'); + } + } +} + +export default SynorCrypto; diff --git a/sdk/js/src/crypto/types.ts b/sdk/js/src/crypto/types.ts new file mode 100644 index 0000000..147a4f2 --- /dev/null +++ b/sdk/js/src/crypto/types.ts @@ -0,0 +1,370 @@ +/** + * Synor Crypto SDK Types + * + * Quantum-resistant cryptographic primitives for the Synor blockchain. + */ + +// ============================================================================ +// Enumerations +// ============================================================================ + +/** Network type for address generation */ +export type Network = 'mainnet' | 'testnet' | 'devnet'; + +/** Supported mnemonic word counts */ +export type MnemonicWordCount = 12 | 15 | 18 | 21 | 24; + +/** Falcon variant selection */ +export type FalconVariant = 'falcon512' | 'falcon1024'; + +/** SPHINCS+ variant selection */ +export type SphincsVariant = 'shake128s' | 'shake192s' | 'shake256s'; + +/** Post-quantum algorithm family */ +export type PqAlgorithm = 'dilithium3' | 'falcon512' | 'falcon1024' | 'sphincs128s' | 'sphincs192s' | 'sphincs256s'; + +/** Algorithm family */ +export type AlgorithmFamily = 'classical' | 'lattice' | 'hash_based' | 'hybrid'; + +// ============================================================================ +// Configuration Types +// ============================================================================ + +/** Crypto SDK configuration */ +export interface CryptoConfig { + apiKey: string; + endpoint?: string; + timeout?: number; + retries?: number; + debug?: boolean; + defaultNetwork?: Network; +} + +/** Key derivation configuration */ +export interface DerivationConfig { + /** Salt for HKDF (optional) */ + salt?: Uint8Array; + /** Info/context for domain separation */ + info?: Uint8Array; + /** Output key length in bytes */ + outputLength?: number; +} + +/** Password derivation configuration */ +export interface PasswordDerivationConfig { + /** Salt (minimum 8 bytes) */ + salt: Uint8Array; + /** Number of PBKDF2 iterations (minimum 10,000) */ + iterations: number; + /** Output key length in bytes (max 64) */ + outputLength?: number; +} + +/** BIP-44 derivation path */ +export interface DerivationPath { + /** Account number (hardened) */ + account: number; + /** Change: 0 = external, 1 = internal */ + change: number; + /** Address index */ + index: number; +} + +// ============================================================================ +// Key Types +// ============================================================================ + +/** Ed25519 public key (32 bytes) */ +export interface Ed25519PublicKey { + bytes: Uint8Array; +} + +/** Dilithium3 public key (~1952 bytes) */ +export interface Dilithium3PublicKey { + bytes: Uint8Array; +} + +/** Hybrid public key (Ed25519 + Dilithium3) */ +export interface HybridPublicKey { + /** Ed25519 component (32 bytes) */ + ed25519: Uint8Array; + /** Dilithium3 component (~1952 bytes) */ + dilithium: Uint8Array; +} + +/** Secret key (master seed) */ +export interface SecretKey { + /** Ed25519 seed (32 bytes) */ + ed25519Seed: Uint8Array; + /** Master seed for derivation (64 bytes) */ + masterSeed: Uint8Array; +} + +/** Falcon public key */ +export interface FalconPublicKey { + variant: FalconVariant; + bytes: Uint8Array; +} + +/** Falcon secret key */ +export interface FalconSecretKey { + variant: FalconVariant; + bytes: Uint8Array; +} + +/** SPHINCS+ public key */ +export interface SphincsPublicKey { + variant: SphincsVariant; + bytes: Uint8Array; +} + +/** SPHINCS+ secret key */ +export interface SphincsSecretKey { + variant: SphincsVariant; + bytes: Uint8Array; +} + +// ============================================================================ +// Signature Types +// ============================================================================ + +/** Ed25519 signature (64 bytes) */ +export interface Ed25519Signature { + bytes: Uint8Array; +} + +/** Dilithium3 signature (~3293 bytes) */ +export interface Dilithium3Signature { + bytes: Uint8Array; +} + +/** Hybrid signature (Ed25519 + Dilithium3) */ +export interface HybridSignature { + /** Ed25519 component (64 bytes) */ + ed25519: Uint8Array; + /** Dilithium3 component (~3293 bytes) */ + dilithium: Uint8Array; +} + +/** Falcon signature */ +export interface FalconSignature { + variant: FalconVariant; + bytes: Uint8Array; +} + +/** SPHINCS+ signature */ +export interface SphincsSignature { + variant: SphincsVariant; + bytes: Uint8Array; +} + +// ============================================================================ +// Keypair Types +// ============================================================================ + +/** Ed25519 keypair */ +export interface Ed25519Keypair { + publicKey: Ed25519PublicKey; + secretKey: Uint8Array; +} + +/** Hybrid keypair (Ed25519 + Dilithium3) */ +export interface HybridKeypair { + publicKey: HybridPublicKey; + secretKey: SecretKey; + /** Get address for this keypair */ + address(network: Network): string; +} + +/** Falcon keypair */ +export interface FalconKeypair { + variant: FalconVariant; + publicKey: FalconPublicKey; + secretKey: FalconSecretKey; +} + +/** SPHINCS+ keypair */ +export interface SphincsKeypair { + variant: SphincsVariant; + publicKey: SphincsPublicKey; + secretKey: SphincsSecretKey; +} + +// ============================================================================ +// Mnemonic Types +// ============================================================================ + +/** Mnemonic phrase representation */ +export interface Mnemonic { + /** The mnemonic phrase */ + phrase: string; + /** Individual words */ + words: string[]; + /** Word count */ + wordCount: MnemonicWordCount; + /** Entropy bytes */ + entropy: Uint8Array; +} + +/** Mnemonic validation result */ +export interface MnemonicValidation { + valid: boolean; + error?: string; +} + +// ============================================================================ +// Address Types +// ============================================================================ + +/** Blockchain address */ +export interface Address { + /** Address string (bech32 encoded) */ + address: string; + /** Network */ + network: Network; + /** Public key hash */ + pubkeyHash: Uint8Array; +} + +// ============================================================================ +// Hash Types +// ============================================================================ + +/** 256-bit hash */ +export interface Hash256 { + bytes: Uint8Array; + hex: string; +} + +// ============================================================================ +// Algorithm Negotiation Types +// ============================================================================ + +/** Algorithm capabilities */ +export interface AlgorithmCapabilities { + /** Supported post-quantum algorithms */ + pqAlgorithms: PqAlgorithm[]; + /** Classical algorithm support */ + classical: boolean; + /** Hybrid mode support */ + hybrid: boolean; + /** Preferred algorithm */ + preferred?: PqAlgorithm; +} + +/** Negotiation policy */ +export interface NegotiationPolicy { + /** Required minimum security level (bits) */ + minSecurityLevel: number; + /** Prefer smaller signatures */ + preferCompact: boolean; + /** Allow classical-only fallback */ + allowClassical: boolean; + /** Required algorithm families */ + requiredFamilies: AlgorithmFamily[]; +} + +/** Negotiation result */ +export interface NegotiationResult { + /** Selected algorithm */ + algorithm: PqAlgorithm; + /** Security level in bits */ + securityLevel: number; + /** Algorithm family */ + family: AlgorithmFamily; + /** Is hybrid mode */ + hybrid: boolean; +} + +/** Session parameters after negotiation */ +export interface SessionParams { + /** Negotiated algorithm */ + algorithm: PqAlgorithm; + /** Session key (if established) */ + sessionKey?: Uint8Array; + /** Expiry timestamp */ + expiresAt?: number; +} + +// ============================================================================ +// Error Types +// ============================================================================ + +/** Crypto error codes */ +export type CryptoErrorCode = + | 'INVALID_MNEMONIC' + | 'INVALID_WORD_COUNT' + | 'INVALID_SEED' + | 'INVALID_KEY' + | 'INVALID_SIGNATURE' + | 'VERIFICATION_FAILED' + | 'DERIVATION_FAILED' + | 'ALGORITHM_MISMATCH' + | 'NEGOTIATION_FAILED' + | 'NETWORK_ERROR' + | 'TIMEOUT'; + +/** Crypto exception */ +export class CryptoError extends Error { + constructor( + message: string, + public readonly code?: CryptoErrorCode, + public readonly cause?: unknown + ) { + super(message); + this.name = 'CryptoError'; + } +} + +// ============================================================================ +// Constants +// ============================================================================ + +export const CryptoConstants = { + /** Ed25519 public key size in bytes */ + ED25519_PUBLIC_KEY_SIZE: 32, + /** Ed25519 secret key size in bytes */ + ED25519_SECRET_KEY_SIZE: 32, + /** Ed25519 signature size in bytes */ + ED25519_SIGNATURE_SIZE: 64, + /** Dilithium3 public key size in bytes */ + DILITHIUM3_PUBLIC_KEY_SIZE: 1952, + /** Dilithium3 signature size in bytes */ + DILITHIUM3_SIGNATURE_SIZE: 3293, + /** Hybrid signature size (Ed25519 + Dilithium3) */ + HYBRID_SIGNATURE_SIZE: 64 + 3293, + /** Falcon-512 signature size */ + FALCON512_SIGNATURE_SIZE: 690, + /** Falcon-512 public key size */ + FALCON512_PUBLIC_KEY_SIZE: 897, + /** Falcon-1024 signature size */ + FALCON1024_SIGNATURE_SIZE: 1330, + /** Falcon-1024 public key size */ + FALCON1024_PUBLIC_KEY_SIZE: 1793, + /** SPHINCS+-128s signature size */ + SPHINCS128S_SIGNATURE_SIZE: 7856, + /** SPHINCS+-192s signature size */ + SPHINCS192S_SIGNATURE_SIZE: 16224, + /** SPHINCS+-256s signature size */ + SPHINCS256S_SIGNATURE_SIZE: 29792, + /** BIP-44 coin type for Synor */ + COIN_TYPE: 0x5359, + /** Minimum PBKDF2 iterations */ + MIN_PBKDF2_ITERATIONS: 10000, + /** Minimum salt length for password derivation */ + MIN_SALT_LENGTH: 8, + /** Default API endpoint */ + DEFAULT_ENDPOINT: 'https://crypto.synor.io/v1', +} as const; + +/** Algorithm size comparison */ +export const AlgorithmSizes = { + ed25519: { signature: 64, publicKey: 32 }, + dilithium3: { signature: 3293, publicKey: 1952 }, + hybrid: { signature: 3357, publicKey: 1984 }, + falcon512: { signature: 690, publicKey: 897 }, + falcon1024: { signature: 1330, publicKey: 1793 }, + sphincs128s: { signature: 7856, publicKey: 32 }, + sphincs192s: { signature: 16224, publicKey: 48 }, + sphincs256s: { signature: 29792, publicKey: 64 }, +} as const; diff --git a/sdk/kotlin/src/main/kotlin/io/synor/crypto/SynorCrypto.kt b/sdk/kotlin/src/main/kotlin/io/synor/crypto/SynorCrypto.kt new file mode 100644 index 0000000..e0cc372 --- /dev/null +++ b/sdk/kotlin/src/main/kotlin/io/synor/crypto/SynorCrypto.kt @@ -0,0 +1,290 @@ +package io.synor.crypto + +import io.ktor.client.* +import io.ktor.client.call.* +import io.ktor.client.engine.cio.* +import io.ktor.client.plugins.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.serialization.kotlinx.json.* +import kotlinx.serialization.json.* +import java.util.Base64 +import java.util.concurrent.atomic.AtomicBoolean + +/** + * Synor Crypto SDK for Kotlin + * + * Quantum-resistant cryptographic primitives for the Synor blockchain. + * + * Example: + * ```kotlin + * val config = CryptoConfig("your-api-key") + * val crypto = SynorCrypto(config) + * + * // Generate a mnemonic + * val mnemonic = crypto.mnemonic().generate(24) + * println("Backup words: ${mnemonic.phrase}") + * + * // Create keypair from mnemonic + * val keypair = crypto.keypairs().fromMnemonic(mnemonic.phrase, "") + * val address = keypair.getAddress(Network.MAINNET) + * + * // Sign a message + * val signature = crypto.signing().sign(keypair, "Hello!".toByteArray()) + * ``` + */ +class SynorCrypto(private val config: CryptoConfig) { + private val httpClient: HttpClient + private val json = Json { + ignoreUnknownKeys = true + namingStrategy = JsonNamingStrategy.SnakeCase + } + private val closed = AtomicBoolean(false) + + private val mnemonicClient: MnemonicClient + private val keypairClient: KeypairClient + private val signingClient: SigningClient + private val falconClient: FalconClient + private val sphincsClient: SphincsClient + private val kdfClient: KdfClient + private val hashClient: HashClient + + init { + httpClient = HttpClient(CIO) { + install(ContentNegotiation) { + json(json) + } + install(HttpTimeout) { + requestTimeoutMillis = config.timeout + connectTimeoutMillis = config.timeout + } + defaultRequest { + header("Authorization", "Bearer ${config.apiKey}") + header("X-SDK-Version", "kotlin/0.1.0") + } + } + + mnemonicClient = MnemonicClient(this) + keypairClient = KeypairClient(this) + signingClient = SigningClient(this) + falconClient = FalconClient(this) + sphincsClient = SphincsClient(this) + kdfClient = KdfClient(this) + hashClient = HashClient(this) + } + + fun getDefaultNetwork(): Network = config.defaultNetwork + + fun mnemonic(): MnemonicClient = mnemonicClient + fun keypairs(): KeypairClient = keypairClient + fun signing(): SigningClient = signingClient + fun falcon(): FalconClient = falconClient + fun sphincs(): SphincsClient = sphincsClient + fun kdf(): KdfClient = kdfClient + fun hash(): HashClient = hashClient + + suspend fun healthCheck(): Boolean = try { + val result = get>("/health") + result["status"] == "healthy" + } catch (e: Exception) { + false + } + + suspend fun getInfo(): Map = get("/info") + + fun close() { + closed.set(true) + httpClient.close() + } + + fun isClosed(): Boolean = closed.get() + + internal suspend inline fun get(path: String): T { + checkClosed() + val response = httpClient.get("${config.endpoint}$path") + return handleResponse(response) + } + + internal suspend inline fun post(path: String, body: Any? = null): T { + checkClosed() + val response = httpClient.post("${config.endpoint}$path") { + contentType(ContentType.Application.Json) + setBody(body ?: emptyMap()) + } + return handleResponse(response) + } + + @PublishedApi + internal suspend inline fun handleResponse(response: HttpResponse): T { + if (response.status.value >= 400) { + val errorBody = response.bodyAsText() + val error = try { + json.decodeFromString>(errorBody) + } catch (e: Exception) { + emptyMap() + } + throw CryptoException( + error["message"] ?: "HTTP ${response.status.value}", + error["code"] + ) + } + return response.body() + } + + private fun checkClosed() { + if (closed.get()) { + throw CryptoException("Client has been closed", "CLIENT_CLOSED") + } + } + + // ======================================================================== + // Sub-Clients + // ======================================================================== + + class MnemonicClient(private val crypto: SynorCrypto) { + suspend fun generate(wordCount: Int = 24): Mnemonic = + crypto.post("/mnemonic/generate", mapOf("word_count" to wordCount)) + + suspend fun fromPhrase(phrase: String): Mnemonic = + crypto.post("/mnemonic/from-phrase", mapOf("phrase" to phrase)) + + suspend fun validate(phrase: String): MnemonicValidation = + crypto.post("/mnemonic/validate", mapOf("phrase" to phrase)) + + suspend fun toSeed(phrase: String, passphrase: String = ""): ByteArray { + val result: Map = crypto.post( + "/mnemonic/to-seed", + mapOf("phrase" to phrase, "passphrase" to passphrase) + ) + return Base64.getDecoder().decode(result["seed"]) + } + + suspend fun suggestWords(partial: String, limit: Int = 5): List { + val result: Map> = crypto.post( + "/mnemonic/suggest", + mapOf("partial" to partial, "limit" to limit) + ) + return result["suggestions"] ?: emptyList() + } + } + + class KeypairClient(private val crypto: SynorCrypto) { + suspend fun generate(): HybridKeypair = + crypto.post("/keypair/generate") + + suspend fun fromMnemonic(phrase: String, passphrase: String = ""): HybridKeypair = + crypto.post("/keypair/from-mnemonic", mapOf("phrase" to phrase, "passphrase" to passphrase)) + + suspend fun fromSeed(seed: ByteArray): HybridKeypair = + crypto.post("/keypair/from-seed", mapOf("seed" to Base64.getEncoder().encodeToString(seed))) + + suspend fun getAddress(publicKey: HybridPublicKey, network: Network): Address = + crypto.post("/keypair/address", mapOf("public_key" to publicKey.toMap(), "network" to network.value)) + } + + class SigningClient(private val crypto: SynorCrypto) { + suspend fun sign(keypair: HybridKeypair, message: ByteArray): HybridSignature = + crypto.post("/sign/hybrid", mapOf( + "secret_key" to keypair.secretKey.toMap(), + "message" to Base64.getEncoder().encodeToString(message) + )) + + suspend fun verify(publicKey: HybridPublicKey, message: ByteArray, signature: HybridSignature): Boolean { + val result: Map = crypto.post("/sign/verify", mapOf( + "public_key" to publicKey.toMap(), + "message" to Base64.getEncoder().encodeToString(message), + "signature" to signature.toMap() + )) + return result["valid"] ?: false + } + + suspend fun signEd25519(secretKey: ByteArray, message: ByteArray): ByteArray { + val result: Map = crypto.post("/sign/ed25519", mapOf( + "secret_key" to Base64.getEncoder().encodeToString(secretKey), + "message" to Base64.getEncoder().encodeToString(message) + )) + return Base64.getDecoder().decode(result["signature"]) + } + } + + class FalconClient(private val crypto: SynorCrypto) { + suspend fun generate(variant: FalconVariant = FalconVariant.FALCON512): FalconKeypair = + crypto.post("/falcon/generate", mapOf("variant" to variant.value)) + + suspend fun sign(keypair: FalconKeypair, message: ByteArray): FalconSignature = + crypto.post("/falcon/sign", mapOf( + "variant" to keypair.getVariantEnum().value, + "secret_key" to Base64.getEncoder().encodeToString(keypair.secretKey.getKeyBytes()), + "message" to Base64.getEncoder().encodeToString(message) + )) + + suspend fun verify(publicKey: ByteArray, message: ByteArray, signature: FalconSignature): Boolean { + val result: Map = crypto.post("/falcon/verify", mapOf( + "variant" to signature.getVariantEnum().value, + "public_key" to Base64.getEncoder().encodeToString(publicKey), + "message" to Base64.getEncoder().encodeToString(message), + "signature" to Base64.getEncoder().encodeToString(signature.getBytes()) + )) + return result["valid"] ?: false + } + } + + class SphincsClient(private val crypto: SynorCrypto) { + suspend fun generate(variant: SphincsVariant = SphincsVariant.SHAKE128S): SphincsKeypair = + crypto.post("/sphincs/generate", mapOf("variant" to variant.value)) + + suspend fun sign(keypair: SphincsKeypair, message: ByteArray): SphincsSignature = + crypto.post("/sphincs/sign", mapOf( + "variant" to keypair.getVariantEnum().value, + "secret_key" to Base64.getEncoder().encodeToString(keypair.secretKey.getKeyBytes()), + "message" to Base64.getEncoder().encodeToString(message) + )) + + suspend fun verify(publicKey: ByteArray, message: ByteArray, signature: SphincsSignature): Boolean { + val result: Map = crypto.post("/sphincs/verify", mapOf( + "variant" to signature.getVariantEnum().value, + "public_key" to Base64.getEncoder().encodeToString(publicKey), + "message" to Base64.getEncoder().encodeToString(message), + "signature" to Base64.getEncoder().encodeToString(signature.getBytes()) + )) + return result["valid"] ?: false + } + } + + class KdfClient(private val crypto: SynorCrypto) { + suspend fun deriveKey(seed: ByteArray, config: DerivationConfig = DerivationConfig()): ByteArray { + val body = mutableMapOf( + "seed" to Base64.getEncoder().encodeToString(seed), + "output_length" to config.outputLength + ) + config.salt?.let { body["salt"] = Base64.getEncoder().encodeToString(it) } + config.info?.let { body["info"] = Base64.getEncoder().encodeToString(it) } + + val result: Map = crypto.post("/kdf/hkdf", body) + return Base64.getDecoder().decode(result["key"]) + } + + suspend fun deriveFromPassword(password: ByteArray, config: PasswordDerivationConfig): ByteArray { + val result: Map = crypto.post("/kdf/pbkdf2", mapOf( + "password" to Base64.getEncoder().encodeToString(password), + "salt" to Base64.getEncoder().encodeToString(config.salt), + "iterations" to config.iterations, + "output_length" to config.outputLength + )) + return Base64.getDecoder().decode(result["key"]) + } + } + + class HashClient(private val crypto: SynorCrypto) { + suspend fun sha3_256(data: ByteArray): Hash256 = + crypto.post("/hash/sha3-256", mapOf("data" to Base64.getEncoder().encodeToString(data))) + + suspend fun blake3(data: ByteArray): Hash256 = + crypto.post("/hash/blake3", mapOf("data" to Base64.getEncoder().encodeToString(data))) + + suspend fun keccak256(data: ByteArray): Hash256 = + crypto.post("/hash/keccak256", mapOf("data" to Base64.getEncoder().encodeToString(data))) + } +} diff --git a/sdk/kotlin/src/main/kotlin/io/synor/crypto/Types.kt b/sdk/kotlin/src/main/kotlin/io/synor/crypto/Types.kt new file mode 100644 index 0000000..8519c12 --- /dev/null +++ b/sdk/kotlin/src/main/kotlin/io/synor/crypto/Types.kt @@ -0,0 +1,353 @@ +package io.synor.crypto + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import java.util.Base64 + +// ============================================================================ +// Enumerations +// ============================================================================ + +@Serializable +enum class Network(val value: String) { + @SerialName("mainnet") MAINNET("mainnet"), + @SerialName("testnet") TESTNET("testnet"), + @SerialName("devnet") DEVNET("devnet"); + + companion object { + fun fromValue(value: String): Network = + entries.find { it.value == value } ?: MAINNET + } +} + +@Serializable +enum class FalconVariant( + val value: String, + val signatureSize: Int, + val publicKeySize: Int, + val securityLevel: Int +) { + @SerialName("falcon512") FALCON512("falcon512", 690, 897, 128), + @SerialName("falcon1024") FALCON1024("falcon1024", 1330, 1793, 256); + + companion object { + fun fromValue(value: String): FalconVariant = + entries.find { it.value == value } ?: FALCON512 + } +} + +@Serializable +enum class SphincsVariant( + val value: String, + val signatureSize: Int, + val securityLevel: Int +) { + @SerialName("shake128s") SHAKE128S("shake128s", 7856, 128), + @SerialName("shake192s") SHAKE192S("shake192s", 16224, 192), + @SerialName("shake256s") SHAKE256S("shake256s", 29792, 256); + + companion object { + fun fromValue(value: String): SphincsVariant = + entries.find { it.value == value } ?: SHAKE128S + } +} + +@Serializable +enum class PqAlgorithm(val value: String) { + @SerialName("dilithium3") DILITHIUM3("dilithium3"), + @SerialName("falcon512") FALCON512("falcon512"), + @SerialName("falcon1024") FALCON1024("falcon1024"), + @SerialName("sphincs128s") SPHINCS128S("sphincs128s"), + @SerialName("sphincs192s") SPHINCS192S("sphincs192s"), + @SerialName("sphincs256s") SPHINCS256S("sphincs256s") +} + +@Serializable +enum class AlgorithmFamily(val value: String) { + @SerialName("classical") CLASSICAL("classical"), + @SerialName("lattice") LATTICE("lattice"), + @SerialName("hash_based") HASH_BASED("hash_based"), + @SerialName("hybrid") HYBRID("hybrid") +} + +// ============================================================================ +// Configuration Types +// ============================================================================ + +data class CryptoConfig( + val apiKey: String, + var endpoint: String = "https://crypto.synor.io/v1", + var timeout: Long = 30000, + var retries: Int = 3, + var debug: Boolean = false, + var defaultNetwork: Network = Network.MAINNET +) + +data class DerivationConfig( + var salt: ByteArray? = null, + var info: ByteArray? = null, + var outputLength: Int = 32 +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is DerivationConfig) return false + return salt.contentEquals(other.salt) && info.contentEquals(other.info) && outputLength == other.outputLength + } + + override fun hashCode(): Int { + var result = salt?.contentHashCode() ?: 0 + result = 31 * result + (info?.contentHashCode() ?: 0) + result = 31 * result + outputLength + return result + } +} + +data class PasswordDerivationConfig( + val salt: ByteArray, + var iterations: Int = 100000, + var outputLength: Int = 32 +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is PasswordDerivationConfig) return false + return salt.contentEquals(other.salt) && iterations == other.iterations && outputLength == other.outputLength + } + + override fun hashCode(): Int { + var result = salt.contentHashCode() + result = 31 * result + iterations + result = 31 * result + outputLength + return result + } +} + +data class DerivationPath( + val account: Int, + val change: Int, + val index: Int +) { + companion object { + const val COIN_TYPE = 0x5359 + + fun external(account: Int, index: Int) = DerivationPath(account, 0, index) + fun internal(account: Int, index: Int) = DerivationPath(account, 1, index) + } + + override fun toString(): String = "m/44'/$COIN_TYPE'/$account'/$change/$index" + + fun toMap(): Map = mapOf("account" to account, "change" to change, "index" to index) +} + +// ============================================================================ +// Key Types +// ============================================================================ + +@Serializable +data class HybridPublicKey( + val ed25519: String, + val dilithium: String +) { + fun getEd25519Bytes(): ByteArray = Base64.getDecoder().decode(ed25519) + fun getDilithiumBytes(): ByteArray = Base64.getDecoder().decode(dilithium) + fun size(): Int = getEd25519Bytes().size + getDilithiumBytes().size + fun toMap(): Map = mapOf("ed25519" to ed25519, "dilithium" to dilithium) +} + +@Serializable +data class SecretKey( + @SerialName("ed25519_seed") val ed25519Seed: String, + @SerialName("master_seed") val masterSeed: String +) { + fun getEd25519SeedBytes(): ByteArray = Base64.getDecoder().decode(ed25519Seed) + fun getMasterSeedBytes(): ByteArray = Base64.getDecoder().decode(masterSeed) + fun toMap(): Map = mapOf("ed25519_seed" to ed25519Seed, "master_seed" to masterSeed) +} + +@Serializable +data class FalconPublicKey( + val variant: String, + val bytes: String +) { + fun getVariantEnum(): FalconVariant = FalconVariant.fromValue(variant) + fun getKeyBytes(): ByteArray = Base64.getDecoder().decode(bytes) +} + +@Serializable +data class FalconSecretKey( + val variant: String, + val bytes: String +) { + fun getVariantEnum(): FalconVariant = FalconVariant.fromValue(variant) + fun getKeyBytes(): ByteArray = Base64.getDecoder().decode(bytes) +} + +@Serializable +data class SphincsPublicKey( + val variant: String, + val bytes: String +) { + fun getVariantEnum(): SphincsVariant = SphincsVariant.fromValue(variant) + fun getKeyBytes(): ByteArray = Base64.getDecoder().decode(bytes) +} + +@Serializable +data class SphincsSecretKey( + val variant: String, + val bytes: String +) { + fun getVariantEnum(): SphincsVariant = SphincsVariant.fromValue(variant) + fun getKeyBytes(): ByteArray = Base64.getDecoder().decode(bytes) +} + +// ============================================================================ +// Signature Types +// ============================================================================ + +@Serializable +data class HybridSignature( + val ed25519: String, + val dilithium: String +) { + fun getEd25519Bytes(): ByteArray = Base64.getDecoder().decode(ed25519) + fun getDilithiumBytes(): ByteArray = Base64.getDecoder().decode(dilithium) + fun size(): Int = getEd25519Bytes().size + getDilithiumBytes().size + + fun toBytes(): ByteArray { + val e = getEd25519Bytes() + val d = getDilithiumBytes() + return e + d + } + + fun toMap(): Map = mapOf("ed25519" to ed25519, "dilithium" to dilithium) +} + +@Serializable +data class FalconSignature( + val variant: String, + val signature: String +) { + fun getVariantEnum(): FalconVariant = FalconVariant.fromValue(variant) + fun getBytes(): ByteArray = Base64.getDecoder().decode(signature) + fun size(): Int = getBytes().size +} + +@Serializable +data class SphincsSignature( + val variant: String, + val signature: String +) { + fun getVariantEnum(): SphincsVariant = SphincsVariant.fromValue(variant) + fun getBytes(): ByteArray = Base64.getDecoder().decode(signature) + fun size(): Int = getBytes().size +} + +// ============================================================================ +// Keypair Types +// ============================================================================ + +@Serializable +data class HybridKeypair( + @SerialName("public_key") val publicKey: HybridPublicKey, + @SerialName("secret_key") val secretKey: SecretKey, + val addresses: Map = emptyMap() +) { + fun getAddress(network: Network): String = addresses[network.value] ?: "" +} + +@Serializable +data class FalconKeypair( + val variant: String, + @SerialName("public_key") val publicKey: FalconPublicKey, + @SerialName("secret_key") val secretKey: FalconSecretKey +) { + fun getVariantEnum(): FalconVariant = FalconVariant.fromValue(variant) +} + +@Serializable +data class SphincsKeypair( + val variant: String, + @SerialName("public_key") val publicKey: SphincsPublicKey, + @SerialName("secret_key") val secretKey: SphincsSecretKey +) { + fun getVariantEnum(): SphincsVariant = SphincsVariant.fromValue(variant) +} + +// ============================================================================ +// Mnemonic Types +// ============================================================================ + +@Serializable +data class Mnemonic( + val phrase: String, + val words: List? = null, + @SerialName("word_count") val wordCount: Int = 0, + val entropy: String? = null +) { + fun getWords(): List = words ?: phrase.split(" ") + fun getWordCount(): Int = if (wordCount > 0) wordCount else getWords().size + fun getEntropy(): ByteArray = entropy?.let { Base64.getDecoder().decode(it) } ?: ByteArray(0) +} + +@Serializable +data class MnemonicValidation( + val valid: Boolean, + val error: String? = null +) + +// ============================================================================ +// Address Types +// ============================================================================ + +@Serializable +data class Address( + val address: String, + val network: String, + @SerialName("pubkey_hash") val pubkeyHash: String? = null +) { + fun getNetwork(): Network = Network.fromValue(network) + fun getPubkeyHashBytes(): ByteArray = pubkeyHash?.let { Base64.getDecoder().decode(it) } ?: ByteArray(0) +} + +// ============================================================================ +// Hash Types +// ============================================================================ + +@Serializable +data class Hash256( + val hash: String +) { + fun getHex(): String = hash + fun getBytes(): ByteArray { + val result = ByteArray(hash.length / 2) + for (i in result.indices) { + result[i] = hash.substring(i * 2, i * 2 + 2).toInt(16).toByte() + } + return result + } +} + +// ============================================================================ +// Error Types +// ============================================================================ + +class CryptoException( + message: String, + val code: String? = null +) : RuntimeException(message) + +// ============================================================================ +// Constants +// ============================================================================ + +object CryptoConstants { + const val ED25519_PUBLIC_KEY_SIZE = 32 + const val ED25519_SECRET_KEY_SIZE = 32 + const val ED25519_SIGNATURE_SIZE = 64 + const val DILITHIUM3_PUBLIC_KEY_SIZE = 1952 + const val DILITHIUM3_SIGNATURE_SIZE = 3293 + const val HYBRID_SIGNATURE_SIZE = 64 + 3293 + const val COIN_TYPE = 0x5359 + const val MIN_PBKDF2_ITERATIONS = 10000 + const val MIN_SALT_LENGTH = 8 + const val DEFAULT_ENDPOINT = "https://crypto.synor.io/v1" +} diff --git a/sdk/python/src/synor_crypto/__init__.py b/sdk/python/src/synor_crypto/__init__.py new file mode 100644 index 0000000..051957f --- /dev/null +++ b/sdk/python/src/synor_crypto/__init__.py @@ -0,0 +1,173 @@ +""" +Synor Crypto SDK for Python + +Quantum-resistant cryptographic primitives for the Synor blockchain. + +Features: +- Hybrid Ed25519 + Dilithium3 signatures (quantum-resistant) +- BIP-39 mnemonic support (12-24 words) +- BIP-44 hierarchical key derivation +- Post-quantum algorithms: Falcon, SPHINCS+ +- Secure key derivation (HKDF, PBKDF2) + +Example: + ```python + from synor_crypto import SynorCrypto, CryptoConfig, Network + + async def main(): + crypto = SynorCrypto(CryptoConfig(api_key='your-api-key')) + + # Generate a mnemonic + mnemonic = await crypto.mnemonic.generate(24) + print(f'Backup words: {mnemonic.phrase}') + + # Create keypair from mnemonic + keypair = await crypto.keypairs.from_mnemonic(mnemonic) + address = keypair.address(Network.MAINNET) + print(f'Address: {address}') + + # Sign a message + message = b'Hello, Synor!' + signature = await crypto.signing.sign(keypair, message) + + # Verify signature + valid = await crypto.signing.verify(keypair.public_key, message, signature) + print(f'Signature valid: {valid}') + + await crypto.close() + ``` +""" + +from .types import ( + # Enumerations + Network, + FalconVariant, + SphincsVariant, + PqAlgorithm, + AlgorithmFamily, + + # Configuration + CryptoConfig, + DerivationConfig, + PasswordDerivationConfig, + DerivationPath, + + # Keys + HybridPublicKey, + SecretKey, + FalconPublicKey, + FalconSecretKey, + SphincsPublicKey, + SphincsSecretKey, + + # Signatures + HybridSignature, + FalconSignature, + SphincsSignature, + + # Keypairs + HybridKeypair, + FalconKeypair, + SphincsKeypair, + + # Mnemonic + Mnemonic, + MnemonicValidation, + + # Address & Hash + Address, + Hash256, + + # Negotiation + AlgorithmCapabilities, + NegotiationPolicy, + NegotiationResult, + SessionParams, + + # Error + CryptoError, + + # Constants + CryptoConstants, + ALGORITHM_SIZES, +) + +from .client import ( + SynorCrypto, + MnemonicClient, + KeypairClient, + SigningClient, + FalconClient, + SphincsClient, + KdfClient, + HashClient, + NegotiationClient, +) + +__version__ = '0.1.0' +__all__ = [ + # Main client + 'SynorCrypto', + + # Sub-clients + 'MnemonicClient', + 'KeypairClient', + 'SigningClient', + 'FalconClient', + 'SphincsClient', + 'KdfClient', + 'HashClient', + 'NegotiationClient', + + # Enumerations + 'Network', + 'FalconVariant', + 'SphincsVariant', + 'PqAlgorithm', + 'AlgorithmFamily', + + # Configuration + 'CryptoConfig', + 'DerivationConfig', + 'PasswordDerivationConfig', + 'DerivationPath', + + # Keys + 'HybridPublicKey', + 'SecretKey', + 'FalconPublicKey', + 'FalconSecretKey', + 'SphincsPublicKey', + 'SphincsSecretKey', + + # Signatures + 'HybridSignature', + 'FalconSignature', + 'SphincsSignature', + + # Keypairs + 'HybridKeypair', + 'FalconKeypair', + 'SphincsKeypair', + + # Mnemonic + 'Mnemonic', + 'MnemonicValidation', + + # Address & Hash + 'Address', + 'Hash256', + + # Negotiation + 'AlgorithmCapabilities', + 'NegotiationPolicy', + 'NegotiationResult', + 'SessionParams', + + # Error + 'CryptoError', + + # Constants + 'CryptoConstants', + 'ALGORITHM_SIZES', +] diff --git a/sdk/python/src/synor_crypto/client.py b/sdk/python/src/synor_crypto/client.py new file mode 100644 index 0000000..6a04983 --- /dev/null +++ b/sdk/python/src/synor_crypto/client.py @@ -0,0 +1,552 @@ +""" +Synor Crypto SDK Client for Python + +Quantum-resistant cryptographic primitives for the Synor blockchain. +""" + +import base64 +from typing import Optional, List, Dict, Any, Union +import httpx + +from .types import ( + CryptoConfig, Network, Mnemonic, MnemonicValidation, + HybridKeypair, HybridPublicKey, HybridSignature, SecretKey, + FalconVariant, FalconKeypair, FalconSignature, + SphincsVariant, SphincsKeypair, SphincsSignature, + DerivationPath, DerivationConfig, PasswordDerivationConfig, + AlgorithmCapabilities, NegotiationPolicy, NegotiationResult, SessionParams, + Hash256, Address, CryptoError +) + + +class MnemonicClient: + """Mnemonic operations.""" + + def __init__(self, crypto: 'SynorCrypto'): + self._crypto = crypto + + async def generate(self, word_count: int = 24) -> Mnemonic: + """Generates a new random mnemonic.""" + result = await self._crypto._post('/mnemonic/generate', {'word_count': word_count}) + return Mnemonic.from_dict(result) + + async def from_phrase(self, phrase: str) -> Mnemonic: + """Creates a mnemonic from a phrase string.""" + result = await self._crypto._post('/mnemonic/from-phrase', {'phrase': phrase}) + return Mnemonic.from_dict(result) + + async def from_entropy(self, entropy: bytes) -> Mnemonic: + """Creates a mnemonic from entropy bytes.""" + result = await self._crypto._post('/mnemonic/from-entropy', { + 'entropy': base64.b64encode(entropy).decode() + }) + return Mnemonic.from_dict(result) + + async def validate(self, phrase: str) -> MnemonicValidation: + """Validates a mnemonic phrase.""" + result = await self._crypto._post('/mnemonic/validate', {'phrase': phrase}) + return MnemonicValidation.from_dict(result) + + async def to_seed(self, mnemonic: Union[Mnemonic, str], passphrase: str = '') -> bytes: + """Derives a 64-byte seed from a mnemonic.""" + phrase = mnemonic.phrase if isinstance(mnemonic, Mnemonic) else mnemonic + result = await self._crypto._post('/mnemonic/to-seed', { + 'phrase': phrase, + 'passphrase': passphrase + }) + return base64.b64decode(result['seed']) + + async def suggest_words(self, partial: str, limit: int = 10) -> List[str]: + """Suggests word completions from the BIP-39 wordlist.""" + result = await self._crypto._post('/mnemonic/suggest', { + 'partial': partial, + 'limit': limit + }) + return result['suggestions'] + + +class KeypairClient: + """Keypair operations.""" + + def __init__(self, crypto: 'SynorCrypto'): + self._crypto = crypto + + async def generate(self) -> HybridKeypair: + """Generates a new random hybrid keypair.""" + result = await self._crypto._post('/keypair/generate') + return HybridKeypair.from_dict(result) + + async def from_mnemonic( + self, + mnemonic: Union[Mnemonic, str], + passphrase: str = '' + ) -> HybridKeypair: + """Creates a keypair from a mnemonic.""" + phrase = mnemonic.phrase if isinstance(mnemonic, Mnemonic) else mnemonic + result = await self._crypto._post('/keypair/from-mnemonic', { + 'phrase': phrase, + 'passphrase': passphrase + }) + return HybridKeypair.from_dict(result) + + async def from_seed(self, seed: bytes) -> HybridKeypair: + """Creates a keypair from a 64-byte seed.""" + result = await self._crypto._post('/keypair/from-seed', { + 'seed': base64.b64encode(seed).decode() + }) + return HybridKeypair.from_dict(result) + + async def derive( + self, + parent_keypair: HybridKeypair, + path: DerivationPath + ) -> HybridKeypair: + """Derives a child keypair using BIP-44 path.""" + result = await self._crypto._post('/keypair/derive', { + 'public_key': parent_keypair.public_key.to_dict(), + 'path': {'account': path.account, 'change': path.change, 'index': path.index} + }) + return HybridKeypair.from_dict(result) + + async def get_address( + self, + public_key: HybridPublicKey, + network: Network + ) -> Address: + """Gets the address for a public key.""" + result = await self._crypto._post('/keypair/address', { + 'public_key': public_key.to_dict(), + 'network': network.value + }) + return Address.from_dict(result) + + +class SigningClient: + """Signing and verification operations.""" + + def __init__(self, crypto: 'SynorCrypto'): + self._crypto = crypto + + async def sign(self, keypair: HybridKeypair, message: bytes) -> HybridSignature: + """Signs a message with a hybrid keypair.""" + result = await self._crypto._post('/sign/hybrid', { + 'secret_key': { + 'ed25519_seed': base64.b64encode(keypair.secret_key.ed25519_seed).decode(), + 'master_seed': base64.b64encode(keypair.secret_key.master_seed).decode() + }, + 'message': base64.b64encode(message).decode() + }) + return HybridSignature.from_dict(result) + + async def verify( + self, + public_key: HybridPublicKey, + message: bytes, + signature: HybridSignature + ) -> bool: + """Verifies a hybrid signature.""" + result = await self._crypto._post('/sign/verify', { + 'public_key': public_key.to_dict(), + 'message': base64.b64encode(message).decode(), + 'signature': { + 'ed25519': base64.b64encode(signature.ed25519).decode(), + 'dilithium': base64.b64encode(signature.dilithium).decode() + } + }) + return result['valid'] + + async def sign_ed25519(self, secret_key: bytes, message: bytes) -> bytes: + """Signs a message using Ed25519 only.""" + result = await self._crypto._post('/sign/ed25519', { + 'secret_key': base64.b64encode(secret_key).decode(), + 'message': base64.b64encode(message).decode() + }) + return base64.b64decode(result['signature']) + + async def verify_ed25519( + self, + public_key: bytes, + message: bytes, + signature: bytes + ) -> bool: + """Verifies an Ed25519 signature.""" + result = await self._crypto._post('/sign/verify-ed25519', { + 'public_key': base64.b64encode(public_key).decode(), + 'message': base64.b64encode(message).decode(), + 'signature': base64.b64encode(signature).decode() + }) + return result['valid'] + + +class FalconClient: + """Falcon (FIPS 206) compact signature operations.""" + + def __init__(self, crypto: 'SynorCrypto'): + self._crypto = crypto + + async def generate(self, variant: FalconVariant = FalconVariant.FALCON512) -> FalconKeypair: + """Generates a Falcon keypair.""" + result = await self._crypto._post('/falcon/generate', {'variant': variant.value}) + return FalconKeypair.from_dict(result) + + async def sign(self, keypair: FalconKeypair, message: bytes) -> FalconSignature: + """Signs a message with Falcon.""" + result = await self._crypto._post('/falcon/sign', { + 'variant': keypair.variant.value, + 'secret_key': base64.b64encode(keypair.secret_key.bytes).decode(), + 'message': base64.b64encode(message).decode() + }) + return FalconSignature.from_dict(result) + + async def verify( + self, + public_key: bytes, + message: bytes, + signature: FalconSignature + ) -> bool: + """Verifies a Falcon signature.""" + result = await self._crypto._post('/falcon/verify', { + 'variant': signature.variant.value, + 'public_key': base64.b64encode(public_key).decode(), + 'message': base64.b64encode(message).decode(), + 'signature': base64.b64encode(signature.bytes).decode() + }) + return result['valid'] + + def signature_size(self, variant: FalconVariant) -> int: + """Returns the signature size for a variant.""" + return 690 if variant == FalconVariant.FALCON512 else 1330 + + def public_key_size(self, variant: FalconVariant) -> int: + """Returns the public key size for a variant.""" + return 897 if variant == FalconVariant.FALCON512 else 1793 + + def security_level(self, variant: FalconVariant) -> int: + """Returns the security level in bits.""" + return 128 if variant == FalconVariant.FALCON512 else 256 + + +class SphincsClient: + """SPHINCS+ (FIPS 205) hash-based signature operations.""" + + def __init__(self, crypto: 'SynorCrypto'): + self._crypto = crypto + + async def generate(self, variant: SphincsVariant = SphincsVariant.SHAKE128S) -> SphincsKeypair: + """Generates a SPHINCS+ keypair.""" + result = await self._crypto._post('/sphincs/generate', {'variant': variant.value}) + return SphincsKeypair.from_dict(result) + + async def sign(self, keypair: SphincsKeypair, message: bytes) -> SphincsSignature: + """Signs a message with SPHINCS+.""" + result = await self._crypto._post('/sphincs/sign', { + 'variant': keypair.variant.value, + 'secret_key': base64.b64encode(keypair.secret_key.bytes).decode(), + 'message': base64.b64encode(message).decode() + }) + return SphincsSignature.from_dict(result) + + async def verify( + self, + public_key: bytes, + message: bytes, + signature: SphincsSignature + ) -> bool: + """Verifies a SPHINCS+ signature.""" + result = await self._crypto._post('/sphincs/verify', { + 'variant': signature.variant.value, + 'public_key': base64.b64encode(public_key).decode(), + 'message': base64.b64encode(message).decode(), + 'signature': base64.b64encode(signature.bytes).decode() + }) + return result['valid'] + + def signature_size(self, variant: SphincsVariant) -> int: + """Returns the signature size for a variant.""" + sizes = { + SphincsVariant.SHAKE128S: 7856, + SphincsVariant.SHAKE192S: 16224, + SphincsVariant.SHAKE256S: 29792, + } + return sizes[variant] + + def security_level(self, variant: SphincsVariant) -> int: + """Returns the security level in bits.""" + levels = { + SphincsVariant.SHAKE128S: 128, + SphincsVariant.SHAKE192S: 192, + SphincsVariant.SHAKE256S: 256, + } + return levels[variant] + + +class KdfClient: + """Key derivation operations.""" + + def __init__(self, crypto: 'SynorCrypto'): + self._crypto = crypto + + async def derive_key( + self, + seed: bytes, + config: Optional[DerivationConfig] = None + ) -> bytes: + """Derives a key using HKDF-SHA3-256.""" + cfg = config or DerivationConfig() + body: Dict[str, Any] = { + 'seed': base64.b64encode(seed).decode(), + 'output_length': cfg.output_length + } + if cfg.salt: + body['salt'] = base64.b64encode(cfg.salt).decode() + if cfg.info: + body['info'] = base64.b64encode(cfg.info).decode() + + result = await self._crypto._post('/kdf/hkdf', body) + return base64.b64decode(result['key']) + + async def derive_from_password( + self, + password: Union[str, bytes], + config: PasswordDerivationConfig + ) -> bytes: + """Derives a key from a password using PBKDF2.""" + password_bytes = password.encode() if isinstance(password, str) else password + result = await self._crypto._post('/kdf/pbkdf2', { + 'password': base64.b64encode(password_bytes).decode(), + 'salt': base64.b64encode(config.salt).decode(), + 'iterations': config.iterations, + 'output_length': config.output_length + }) + return base64.b64decode(result['key']) + + async def derive_ed25519_key(self, master_seed: bytes, account: int) -> bytes: + """Derives an Ed25519 key from a master seed.""" + result = await self._crypto._post('/kdf/ed25519', { + 'master_seed': base64.b64encode(master_seed).decode(), + 'account': account + }) + return base64.b64decode(result['key']) + + async def derive_child_key( + self, + parent_key: bytes, + chain_code: bytes, + index: int + ) -> tuple[bytes, bytes]: + """Derives a child key using BIP-32 style derivation.""" + result = await self._crypto._post('/kdf/child', { + 'parent_key': base64.b64encode(parent_key).decode(), + 'chain_code': base64.b64encode(chain_code).decode(), + 'index': index + }) + return ( + base64.b64decode(result['key']), + base64.b64decode(result['chain_code']) + ) + + def create_path( + self, + account: int, + change: int = 0, + index: int = 0 + ) -> DerivationPath: + """Creates a BIP-44 derivation path.""" + return DerivationPath(account=account, change=change, index=index) + + +class HashClient: + """Hashing operations.""" + + def __init__(self, crypto: 'SynorCrypto'): + self._crypto = crypto + + async def sha3_256(self, data: bytes) -> Hash256: + """Computes SHA3-256 hash.""" + result = await self._crypto._post('/hash/sha3-256', { + 'data': base64.b64encode(data).decode() + }) + return Hash256.from_dict(result) + + async def blake3(self, data: bytes) -> Hash256: + """Computes BLAKE3 hash.""" + result = await self._crypto._post('/hash/blake3', { + 'data': base64.b64encode(data).decode() + }) + return Hash256.from_dict(result) + + async def keccak256(self, data: bytes) -> Hash256: + """Computes Keccak-256 hash.""" + result = await self._crypto._post('/hash/keccak256', { + 'data': base64.b64encode(data).decode() + }) + return Hash256.from_dict(result) + + async def combine(self, hashes: List[bytes]) -> Hash256: + """Combines multiple hashes.""" + result = await self._crypto._post('/hash/combine', { + 'hashes': [base64.b64encode(h).decode() for h in hashes] + }) + return Hash256.from_dict(result) + + +class NegotiationClient: + """Algorithm negotiation operations.""" + + def __init__(self, crypto: 'SynorCrypto'): + self._crypto = crypto + + async def get_capabilities(self) -> AlgorithmCapabilities: + """Gets the local capabilities.""" + result = await self._crypto._get('/negotiation/capabilities') + return AlgorithmCapabilities.from_dict(result) + + async def negotiate( + self, + peer_capabilities: AlgorithmCapabilities, + policy: NegotiationPolicy + ) -> NegotiationResult: + """Negotiates algorithm selection with a peer.""" + result = await self._crypto._post('/negotiation/negotiate', { + 'peer_capabilities': { + 'pq_algorithms': [a.value for a in peer_capabilities.pq_algorithms], + 'classical': peer_capabilities.classical, + 'hybrid': peer_capabilities.hybrid, + 'preferred': peer_capabilities.preferred.value if peer_capabilities.preferred else None + }, + 'policy': { + 'min_security_level': policy.min_security_level, + 'prefer_compact': policy.prefer_compact, + 'allow_classical': policy.allow_classical, + 'required_families': [f.value for f in policy.required_families] + } + }) + return NegotiationResult.from_dict(result) + + async def establish_session( + self, + result: NegotiationResult, + peer_public_key: bytes + ) -> SessionParams: + """Establishes session parameters after negotiation.""" + resp = await self._crypto._post('/negotiation/session', { + 'negotiation_result': { + 'algorithm': result.algorithm.value, + 'security_level': result.security_level, + 'family': result.family.value, + 'hybrid': result.hybrid + }, + 'peer_public_key': base64.b64encode(peer_public_key).decode() + }) + return SessionParams.from_dict(resp) + + +class SynorCrypto: + """ + Synor Crypto SDK Client + + Provides quantum-resistant cryptographic operations for the Synor blockchain. + + Example: + ```python + from synor_crypto import SynorCrypto, CryptoConfig + + async def main(): + crypto = SynorCrypto(CryptoConfig(api_key='your-api-key')) + + # Generate a mnemonic + mnemonic = await crypto.mnemonic.generate(24) + print(f'Backup words: {mnemonic.phrase}') + + # Create keypair from mnemonic + keypair = await crypto.keypairs.from_mnemonic(mnemonic) + address = keypair.address(Network.MAINNET) + + # Sign a message + signature = await crypto.signing.sign(keypair, b'Hello!') + + await crypto.close() + ``` + """ + + def __init__(self, config: CryptoConfig): + self._config = config + self._client = httpx.AsyncClient( + base_url=config.endpoint, + timeout=config.timeout / 1000, + headers={ + 'Authorization': f'Bearer {config.api_key}', + 'Content-Type': 'application/json', + 'X-SDK-Version': 'python/0.1.0' + } + ) + self._closed = False + + self.mnemonic = MnemonicClient(self) + self.keypairs = KeypairClient(self) + self.signing = SigningClient(self) + self.falcon = FalconClient(self) + self.sphincs = SphincsClient(self) + self.kdf = KdfClient(self) + self.hash = HashClient(self) + self.negotiation = NegotiationClient(self) + + @property + def default_network(self) -> Network: + """Returns the default network.""" + return self._config.default_network + + async def health_check(self) -> bool: + """Checks if the service is healthy.""" + try: + result = await self._get('/health') + return result.get('status') == 'healthy' + except Exception: + return False + + async def get_info(self) -> Dict[str, Any]: + """Gets service information.""" + return await self._get('/info') + + async def close(self) -> None: + """Closes the client.""" + self._closed = True + await self._client.aclose() + + @property + def is_closed(self) -> bool: + """Returns True if the client is closed.""" + return self._closed + + async def __aenter__(self) -> 'SynorCrypto': + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: + await self.close() + + async def _get(self, path: str) -> Dict[str, Any]: + """Makes a GET request.""" + self._check_closed() + response = await self._client.get(path) + return self._handle_response(response) + + async def _post(self, path: str, body: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: + """Makes a POST request.""" + self._check_closed() + response = await self._client.post(path, json=body or {}) + return self._handle_response(response) + + def _handle_response(self, response: httpx.Response) -> Dict[str, Any]: + """Handles the HTTP response.""" + data = response.json() + if not response.is_success: + raise CryptoError( + data.get('message', f'HTTP {response.status_code}'), + data.get('code') + ) + return data + + def _check_closed(self) -> None: + """Checks if the client is closed.""" + if self._closed: + raise CryptoError('Client has been closed', 'CLIENT_CLOSED') diff --git a/sdk/python/src/synor_crypto/types.py b/sdk/python/src/synor_crypto/types.py new file mode 100644 index 0000000..a93696c --- /dev/null +++ b/sdk/python/src/synor_crypto/types.py @@ -0,0 +1,526 @@ +""" +Synor Crypto SDK Types + +Quantum-resistant cryptographic types for the Synor blockchain. +""" + +from dataclasses import dataclass, field +from enum import Enum +from typing import List, Optional, Dict, Callable +import base64 + + +# ============================================================================ +# Enumerations +# ============================================================================ + +class Network(str, Enum): + """Network type for address generation.""" + MAINNET = "mainnet" + TESTNET = "testnet" + DEVNET = "devnet" + + +class FalconVariant(str, Enum): + """Falcon variant selection.""" + FALCON512 = "falcon512" # 128-bit security, ~690 byte signatures + FALCON1024 = "falcon1024" # 256-bit security, ~1330 byte signatures + + +class SphincsVariant(str, Enum): + """SPHINCS+ variant selection.""" + SHAKE128S = "shake128s" # 128-bit security, ~7.8KB signatures + SHAKE192S = "shake192s" # 192-bit security, ~16KB signatures + SHAKE256S = "shake256s" # 256-bit security, ~30KB signatures + + +class PqAlgorithm(str, Enum): + """Post-quantum algorithm selection.""" + DILITHIUM3 = "dilithium3" + FALCON512 = "falcon512" + FALCON1024 = "falcon1024" + SPHINCS128S = "sphincs128s" + SPHINCS192S = "sphincs192s" + SPHINCS256S = "sphincs256s" + + +class AlgorithmFamily(str, Enum): + """Algorithm family classification.""" + CLASSICAL = "classical" + LATTICE = "lattice" + HASH_BASED = "hash_based" + HYBRID = "hybrid" + + +# ============================================================================ +# Configuration Types +# ============================================================================ + +@dataclass +class CryptoConfig: + """Crypto SDK configuration.""" + api_key: str + endpoint: str = "https://crypto.synor.io/v1" + timeout: int = 30000 + retries: int = 3 + debug: bool = False + default_network: Network = Network.MAINNET + + +@dataclass +class DerivationConfig: + """Key derivation configuration.""" + salt: Optional[bytes] = None + info: Optional[bytes] = None + output_length: int = 32 + + +@dataclass +class PasswordDerivationConfig: + """Password derivation configuration.""" + salt: bytes = field(default_factory=bytes) + iterations: int = 100000 + output_length: int = 32 + + +@dataclass +class DerivationPath: + """BIP-44 derivation path.""" + account: int = 0 + change: int = 0 + index: int = 0 + + COIN_TYPE: int = 0x5359 # Synor coin type + + def __str__(self) -> str: + return f"m/44'/{self.COIN_TYPE}'/{self.account}'/{self.change}/{self.index}" + + @classmethod + def external(cls, account: int, index: int) -> 'DerivationPath': + """Creates a path for external addresses.""" + return cls(account=account, change=0, index=index) + + @classmethod + def internal(cls, account: int, index: int) -> 'DerivationPath': + """Creates a path for internal (change) addresses.""" + return cls(account=account, change=1, index=index) + + +# ============================================================================ +# Key Types +# ============================================================================ + +@dataclass +class HybridPublicKey: + """Hybrid public key (Ed25519 + Dilithium3).""" + ed25519: bytes # 32 bytes + dilithium: bytes # ~1952 bytes + + @classmethod + def from_dict(cls, data: dict) -> 'HybridPublicKey': + return cls( + ed25519=base64.b64decode(data['ed25519']), + dilithium=base64.b64decode(data['dilithium']) + ) + + def to_dict(self) -> dict: + return { + 'ed25519': base64.b64encode(self.ed25519).decode(), + 'dilithium': base64.b64encode(self.dilithium).decode() + } + + def size(self) -> int: + """Returns the total size in bytes.""" + return len(self.ed25519) + len(self.dilithium) + + +@dataclass +class SecretKey: + """Secret key (master seed).""" + ed25519_seed: bytes # 32 bytes + master_seed: bytes # 64 bytes + + @classmethod + def from_dict(cls, data: dict) -> 'SecretKey': + return cls( + ed25519_seed=base64.b64decode(data['ed25519_seed']), + master_seed=base64.b64decode(data['master_seed']) + ) + + +@dataclass +class FalconPublicKey: + """Falcon public key.""" + variant: FalconVariant + bytes: bytes + + @classmethod + def from_dict(cls, data: dict) -> 'FalconPublicKey': + return cls( + variant=FalconVariant(data['variant']), + bytes=base64.b64decode(data['bytes']) + ) + + +@dataclass +class FalconSecretKey: + """Falcon secret key.""" + variant: FalconVariant + bytes: bytes + + @classmethod + def from_dict(cls, data: dict) -> 'FalconSecretKey': + return cls( + variant=FalconVariant(data['variant']), + bytes=base64.b64decode(data['bytes']) + ) + + +@dataclass +class SphincsPublicKey: + """SPHINCS+ public key.""" + variant: SphincsVariant + bytes: bytes + + @classmethod + def from_dict(cls, data: dict) -> 'SphincsPublicKey': + return cls( + variant=SphincsVariant(data['variant']), + bytes=base64.b64decode(data['bytes']) + ) + + +@dataclass +class SphincsSecretKey: + """SPHINCS+ secret key.""" + variant: SphincsVariant + bytes: bytes + + @classmethod + def from_dict(cls, data: dict) -> 'SphincsSecretKey': + return cls( + variant=SphincsVariant(data['variant']), + bytes=base64.b64decode(data['bytes']) + ) + + +# ============================================================================ +# Signature Types +# ============================================================================ + +@dataclass +class HybridSignature: + """Hybrid signature (Ed25519 + Dilithium3).""" + ed25519: bytes # 64 bytes + dilithium: bytes # ~3293 bytes + + @classmethod + def from_dict(cls, data: dict) -> 'HybridSignature': + return cls( + ed25519=base64.b64decode(data['ed25519']), + dilithium=base64.b64decode(data['dilithium']) + ) + + def to_bytes(self) -> bytes: + """Serializes the signature to bytes.""" + return self.ed25519 + self.dilithium + + @classmethod + def from_bytes(cls, data: bytes) -> 'HybridSignature': + """Deserializes a signature from bytes.""" + if len(data) < 64: + raise ValueError("Invalid signature length") + return cls( + ed25519=data[:64], + dilithium=data[64:] + ) + + def size(self) -> int: + """Returns the total size in bytes.""" + return len(self.ed25519) + len(self.dilithium) + + +@dataclass +class FalconSignature: + """Falcon signature.""" + variant: FalconVariant + bytes: bytes + + @classmethod + def from_dict(cls, data: dict) -> 'FalconSignature': + return cls( + variant=FalconVariant(data['variant']), + bytes=base64.b64decode(data['signature']) + ) + + def size(self) -> int: + return len(self.bytes) + + +@dataclass +class SphincsSignature: + """SPHINCS+ signature.""" + variant: SphincsVariant + bytes: bytes + + @classmethod + def from_dict(cls, data: dict) -> 'SphincsSignature': + return cls( + variant=SphincsVariant(data['variant']), + bytes=base64.b64decode(data['signature']) + ) + + def size(self) -> int: + return len(self.bytes) + + +# ============================================================================ +# Keypair Types +# ============================================================================ + +@dataclass +class HybridKeypair: + """Hybrid keypair (Ed25519 + Dilithium3).""" + public_key: HybridPublicKey + secret_key: SecretKey + _addresses: Dict[str, str] = field(default_factory=dict) + + def address(self, network: Network) -> str: + """Returns the address for the given network.""" + return self._addresses.get(network.value, "") + + @classmethod + def from_dict(cls, data: dict) -> 'HybridKeypair': + return cls( + public_key=HybridPublicKey.from_dict(data['public_key']), + secret_key=SecretKey.from_dict(data['secret_key']), + _addresses=data.get('addresses', {}) + ) + + +@dataclass +class FalconKeypair: + """Falcon keypair.""" + variant: FalconVariant + public_key: FalconPublicKey + secret_key: FalconSecretKey + + @classmethod + def from_dict(cls, data: dict) -> 'FalconKeypair': + return cls( + variant=FalconVariant(data['variant']), + public_key=FalconPublicKey.from_dict(data['public_key']), + secret_key=FalconSecretKey.from_dict(data['secret_key']) + ) + + +@dataclass +class SphincsKeypair: + """SPHINCS+ keypair.""" + variant: SphincsVariant + public_key: SphincsPublicKey + secret_key: SphincsSecretKey + + @classmethod + def from_dict(cls, data: dict) -> 'SphincsKeypair': + return cls( + variant=SphincsVariant(data['variant']), + public_key=SphincsPublicKey.from_dict(data['public_key']), + secret_key=SphincsSecretKey.from_dict(data['secret_key']) + ) + + +# ============================================================================ +# Mnemonic Types +# ============================================================================ + +@dataclass +class Mnemonic: + """BIP-39 mnemonic phrase.""" + phrase: str + words: List[str] = field(default_factory=list) + word_count: int = 24 + entropy: bytes = field(default_factory=bytes) + + def __post_init__(self): + if not self.words: + self.words = self.phrase.split() + if not self.word_count: + self.word_count = len(self.words) + + @classmethod + def from_dict(cls, data: dict) -> 'Mnemonic': + return cls( + phrase=data['phrase'], + words=data.get('words', data['phrase'].split()), + word_count=data.get('word_count', len(data['phrase'].split())), + entropy=base64.b64decode(data.get('entropy', '')) if data.get('entropy') else b'' + ) + + +@dataclass +class MnemonicValidation: + """Mnemonic validation result.""" + valid: bool + error: Optional[str] = None + + @classmethod + def from_dict(cls, data: dict) -> 'MnemonicValidation': + return cls( + valid=data['valid'], + error=data.get('error') + ) + + +# ============================================================================ +# Address Types +# ============================================================================ + +@dataclass +class Address: + """Blockchain address.""" + address: str + network: Network + pubkey_hash: bytes = field(default_factory=bytes) + + @classmethod + def from_dict(cls, data: dict) -> 'Address': + return cls( + address=data['address'], + network=Network(data['network']), + pubkey_hash=base64.b64decode(data.get('pubkey_hash', '')) if data.get('pubkey_hash') else b'' + ) + + +# ============================================================================ +# Hash Types +# ============================================================================ + +@dataclass +class Hash256: + """256-bit hash.""" + bytes: bytes + hex: str + + @classmethod + def from_dict(cls, data: dict) -> 'Hash256': + hex_str = data['hash'] + return cls( + bytes=bytes.fromhex(hex_str), + hex=hex_str + ) + + +# ============================================================================ +# Negotiation Types +# ============================================================================ + +@dataclass +class AlgorithmCapabilities: + """Algorithm capabilities for negotiation.""" + pq_algorithms: List[PqAlgorithm] + classical: bool = True + hybrid: bool = True + preferred: Optional[PqAlgorithm] = None + + @classmethod + def from_dict(cls, data: dict) -> 'AlgorithmCapabilities': + return cls( + pq_algorithms=[PqAlgorithm(a) for a in data.get('pq_algorithms', [])], + classical=data.get('classical', True), + hybrid=data.get('hybrid', True), + preferred=PqAlgorithm(data['preferred']) if data.get('preferred') else None + ) + + +@dataclass +class NegotiationPolicy: + """Negotiation policy.""" + min_security_level: int = 128 + prefer_compact: bool = False + allow_classical: bool = False + required_families: List[AlgorithmFamily] = field(default_factory=list) + + +@dataclass +class NegotiationResult: + """Negotiation result.""" + algorithm: PqAlgorithm + security_level: int + family: AlgorithmFamily + hybrid: bool + + @classmethod + def from_dict(cls, data: dict) -> 'NegotiationResult': + return cls( + algorithm=PqAlgorithm(data['algorithm']), + security_level=data['security_level'], + family=AlgorithmFamily(data['family']), + hybrid=data['hybrid'] + ) + + +@dataclass +class SessionParams: + """Session parameters after negotiation.""" + algorithm: PqAlgorithm + session_key: Optional[bytes] = None + expires_at: Optional[int] = None + + @classmethod + def from_dict(cls, data: dict) -> 'SessionParams': + return cls( + algorithm=PqAlgorithm(data['algorithm']), + session_key=base64.b64decode(data['session_key']) if data.get('session_key') else None, + expires_at=data.get('expires_at') + ) + + +# ============================================================================ +# Error Types +# ============================================================================ + +class CryptoError(Exception): + """Crypto operation error.""" + + def __init__(self, message: str, code: Optional[str] = None): + super().__init__(message) + self.code = code + + +# ============================================================================ +# Constants +# ============================================================================ + +class CryptoConstants: + """Crypto constants.""" + ED25519_PUBLIC_KEY_SIZE = 32 + ED25519_SECRET_KEY_SIZE = 32 + ED25519_SIGNATURE_SIZE = 64 + DILITHIUM3_PUBLIC_KEY_SIZE = 1952 + DILITHIUM3_SIGNATURE_SIZE = 3293 + HYBRID_SIGNATURE_SIZE = 64 + 3293 + FALCON512_SIGNATURE_SIZE = 690 + FALCON512_PUBLIC_KEY_SIZE = 897 + FALCON1024_SIGNATURE_SIZE = 1330 + FALCON1024_PUBLIC_KEY_SIZE = 1793 + SPHINCS128S_SIGNATURE_SIZE = 7856 + SPHINCS192S_SIGNATURE_SIZE = 16224 + SPHINCS256S_SIGNATURE_SIZE = 29792 + COIN_TYPE = 0x5359 + MIN_PBKDF2_ITERATIONS = 10000 + MIN_SALT_LENGTH = 8 + DEFAULT_ENDPOINT = "https://crypto.synor.io/v1" + + +# Algorithm size comparison +ALGORITHM_SIZES = { + 'ed25519': {'signature': 64, 'public_key': 32}, + 'dilithium3': {'signature': 3293, 'public_key': 1952}, + 'hybrid': {'signature': 3357, 'public_key': 1984}, + 'falcon512': {'signature': 690, 'public_key': 897}, + 'falcon1024': {'signature': 1330, 'public_key': 1793}, + 'sphincs128s': {'signature': 7856, 'public_key': 32}, + 'sphincs192s': {'signature': 16224, 'public_key': 48}, + 'sphincs256s': {'signature': 29792, 'public_key': 64}, +} diff --git a/sdk/ruby/lib/synor/crypto.rb b/sdk/ruby/lib/synor/crypto.rb new file mode 100644 index 0000000..47c2ce6 --- /dev/null +++ b/sdk/ruby/lib/synor/crypto.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require_relative 'crypto/types' +require_relative 'crypto/client' + +module Synor + # Synor Crypto SDK for Ruby + # + # Quantum-resistant cryptographic primitives for the Synor blockchain. + module Crypto + VERSION = '0.1.0' + end +end diff --git a/sdk/ruby/lib/synor/crypto/client.rb b/sdk/ruby/lib/synor/crypto/client.rb new file mode 100644 index 0000000..8efea12 --- /dev/null +++ b/sdk/ruby/lib/synor/crypto/client.rb @@ -0,0 +1,334 @@ +# frozen_string_literal: true + +require 'net/http' +require 'json' +require 'uri' +require 'base64' + +require_relative 'types' + +module Synor + module Crypto + # Synor Crypto SDK for Ruby + # + # Quantum-resistant cryptographic primitives for the Synor blockchain. + # + # @example + # config = Synor::Crypto::CryptoConfig.new(api_key: 'your-api-key') + # crypto = Synor::Crypto::SynorCrypto.new(config) + # + # # Generate a mnemonic + # mnemonic = crypto.mnemonic.generate(24) + # puts "Backup words: #{mnemonic.phrase}" + # + # # Create keypair from mnemonic + # keypair = crypto.keypairs.from_mnemonic(mnemonic.phrase, '') + # address = keypair.get_address(Synor::Crypto::Network::MAINNET) + # + # # Sign a message + # signature = crypto.signing.sign(keypair, 'Hello!') + # + class SynorCrypto + attr_reader :config, :mnemonic, :keypairs, :signing, :falcon, :sphincs, :kdf, :hash + + def initialize(config) + @config = config + @closed = false + + @mnemonic = MnemonicClient.new(self) + @keypairs = KeypairClient.new(self) + @signing = SigningClient.new(self) + @falcon = FalconClient.new(self) + @sphincs = SphincsClient.new(self) + @kdf = KdfClient.new(self) + @hash = HashClient.new(self) + end + + def default_network + config.default_network + end + + def health_check + result = get('/health') + result['status'] == 'healthy' + rescue StandardError + false + end + + def get_info + get('/info') + end + + def close + @closed = true + end + + def closed? + @closed + end + + # @api private + def get(path) + check_closed! + request(:get, path) + end + + # @api private + def post(path, body = {}) + check_closed! + request(:post, path, body) + end + + private + + def request(method, path, body = nil) + uri = URI.parse("#{config.endpoint}#{path}") + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = uri.scheme == 'https' + http.open_timeout = config.timeout / 1000.0 + http.read_timeout = config.timeout / 1000.0 + + request = case method + when :get + Net::HTTP::Get.new(uri.request_uri) + when :post + req = Net::HTTP::Post.new(uri.request_uri) + req.body = JSON.generate(body || {}) + req + end + + request['Authorization'] = "Bearer #{config.api_key}" + request['Content-Type'] = 'application/json' + request['X-SDK-Version'] = 'ruby/0.1.0' + + response = http.request(request) + handle_response(response) + end + + def handle_response(response) + body = JSON.parse(response.body) + + unless response.is_a?(Net::HTTPSuccess) + message = body['message'] || "HTTP #{response.code}" + code = body['code'] + raise CryptoError.new(message, code: code, http_status: response.code.to_i) + end + + body + end + + def check_closed! + raise CryptoError.new('Client has been closed', code: 'CLIENT_CLOSED') if @closed + end + end + + # Mnemonic sub-client + class MnemonicClient + def initialize(crypto) + @crypto = crypto + end + + def generate(word_count = 24) + result = @crypto.post('/mnemonic/generate', { word_count: word_count }) + Mnemonic.from_hash(result) + end + + def from_phrase(phrase) + result = @crypto.post('/mnemonic/from-phrase', { phrase: phrase }) + Mnemonic.from_hash(result) + end + + def validate(phrase) + result = @crypto.post('/mnemonic/validate', { phrase: phrase }) + MnemonicValidation.from_hash(result) + end + + def to_seed(phrase, passphrase = '') + result = @crypto.post('/mnemonic/to-seed', { phrase: phrase, passphrase: passphrase }) + Base64.strict_decode64(result['seed']) + end + + def suggest_words(partial, limit = 5) + result = @crypto.post('/mnemonic/suggest', { partial: partial, limit: limit }) + result['suggestions'] || [] + end + end + + # Keypair sub-client + class KeypairClient + def initialize(crypto) + @crypto = crypto + end + + def generate + result = @crypto.post('/keypair/generate') + HybridKeypair.from_hash(result) + end + + def from_mnemonic(phrase, passphrase = '') + result = @crypto.post('/keypair/from-mnemonic', { phrase: phrase, passphrase: passphrase }) + HybridKeypair.from_hash(result) + end + + def from_seed(seed) + result = @crypto.post('/keypair/from-seed', { seed: Base64.strict_encode64(seed) }) + HybridKeypair.from_hash(result) + end + + def get_address(public_key, network) + result = @crypto.post('/keypair/address', { + public_key: public_key.to_h, + network: network + }) + Address.from_hash(result) + end + end + + # Signing sub-client + class SigningClient + def initialize(crypto) + @crypto = crypto + end + + def sign(keypair, message) + message_bytes = message.is_a?(String) ? message.encode('UTF-8').bytes.pack('C*') : message + result = @crypto.post('/sign/hybrid', { + secret_key: keypair.secret_key.to_h, + message: Base64.strict_encode64(message_bytes) + }) + HybridSignature.from_hash(result) + end + + def verify(public_key, message, signature) + message_bytes = message.is_a?(String) ? message.encode('UTF-8').bytes.pack('C*') : message + result = @crypto.post('/sign/verify', { + public_key: public_key.to_h, + message: Base64.strict_encode64(message_bytes), + signature: signature.to_h + }) + result['valid'] == true + end + + def sign_ed25519(secret_key, message) + result = @crypto.post('/sign/ed25519', { + secret_key: Base64.strict_encode64(secret_key), + message: Base64.strict_encode64(message) + }) + Base64.strict_decode64(result['signature']) + end + end + + # Falcon sub-client + class FalconClient + def initialize(crypto) + @crypto = crypto + end + + def generate(variant = FalconVariant::FALCON512) + result = @crypto.post('/falcon/generate', { variant: variant }) + FalconKeypair.from_hash(result) + end + + def sign(keypair, message) + result = @crypto.post('/falcon/sign', { + variant: keypair.variant, + secret_key: Base64.strict_encode64(keypair.secret_key.key_bytes), + message: Base64.strict_encode64(message) + }) + FalconSignature.from_hash(result) + end + + def verify(public_key, message, signature) + result = @crypto.post('/falcon/verify', { + variant: signature.variant, + public_key: Base64.strict_encode64(public_key), + message: Base64.strict_encode64(message), + signature: Base64.strict_encode64(signature.signature_bytes) + }) + result['valid'] == true + end + end + + # SPHINCS+ sub-client + class SphincsClient + def initialize(crypto) + @crypto = crypto + end + + def generate(variant = SphincsVariant::SHAKE128S) + result = @crypto.post('/sphincs/generate', { variant: variant }) + SphincsKeypair.from_hash(result) + end + + def sign(keypair, message) + result = @crypto.post('/sphincs/sign', { + variant: keypair.variant, + secret_key: Base64.strict_encode64(keypair.secret_key.key_bytes), + message: Base64.strict_encode64(message) + }) + SphincsSignature.from_hash(result) + end + + def verify(public_key, message, signature) + result = @crypto.post('/sphincs/verify', { + variant: signature.variant, + public_key: Base64.strict_encode64(public_key), + message: Base64.strict_encode64(message), + signature: Base64.strict_encode64(signature.signature_bytes) + }) + result['valid'] == true + end + end + + # KDF sub-client + class KdfClient + def initialize(crypto) + @crypto = crypto + end + + def derive_key(seed, config = DerivationConfig.new) + body = { + seed: Base64.strict_encode64(seed), + output_length: config.output_length + } + body[:salt] = Base64.strict_encode64(config.salt) if config.salt + body[:info] = Base64.strict_encode64(config.info) if config.info + + result = @crypto.post('/kdf/hkdf', body) + Base64.strict_decode64(result['key']) + end + + def derive_from_password(password, config) + result = @crypto.post('/kdf/pbkdf2', { + password: Base64.strict_encode64(password), + salt: Base64.strict_encode64(config.salt), + iterations: config.iterations, + output_length: config.output_length + }) + Base64.strict_decode64(result['key']) + end + end + + # Hash sub-client + class HashClient + def initialize(crypto) + @crypto = crypto + end + + def sha3_256(data) + result = @crypto.post('/hash/sha3-256', { data: Base64.strict_encode64(data) }) + Hash256.from_hash(result) + end + + def blake3(data) + result = @crypto.post('/hash/blake3', { data: Base64.strict_encode64(data) }) + Hash256.from_hash(result) + end + + def keccak256(data) + result = @crypto.post('/hash/keccak256', { data: Base64.strict_encode64(data) }) + Hash256.from_hash(result) + end + end + end +end diff --git a/sdk/ruby/lib/synor/crypto/types.rb b/sdk/ruby/lib/synor/crypto/types.rb new file mode 100644 index 0000000..b1709db --- /dev/null +++ b/sdk/ruby/lib/synor/crypto/types.rb @@ -0,0 +1,576 @@ +# frozen_string_literal: true + +require 'base64' + +module Synor + module Crypto + # ============================================================================ + # Constants + # ============================================================================ + + ED25519_PUBLIC_KEY_SIZE = 32 + ED25519_SECRET_KEY_SIZE = 32 + ED25519_SIGNATURE_SIZE = 64 + DILITHIUM3_PUBLIC_KEY_SIZE = 1952 + DILITHIUM3_SIGNATURE_SIZE = 3293 + HYBRID_SIGNATURE_SIZE = 64 + 3293 + COIN_TYPE = 0x5359 + MIN_PBKDF2_ITERATIONS = 10_000 + MIN_SALT_LENGTH = 8 + DEFAULT_ENDPOINT = 'https://crypto.synor.io/v1' + + # ============================================================================ + # Enumerations + # ============================================================================ + + module Network + MAINNET = 'mainnet' + TESTNET = 'testnet' + DEVNET = 'devnet' + + def self.all + [MAINNET, TESTNET, DEVNET] + end + end + + module FalconVariant + FALCON512 = 'falcon512' + FALCON1024 = 'falcon1024' + + SIGNATURE_SIZES = { + FALCON512 => 690, + FALCON1024 => 1330 + }.freeze + + PUBLIC_KEY_SIZES = { + FALCON512 => 897, + FALCON1024 => 1793 + }.freeze + + SECURITY_LEVELS = { + FALCON512 => 128, + FALCON1024 => 256 + }.freeze + + def self.signature_size(variant) + SIGNATURE_SIZES[variant] || 690 + end + + def self.public_key_size(variant) + PUBLIC_KEY_SIZES[variant] || 897 + end + + def self.security_level(variant) + SECURITY_LEVELS[variant] || 128 + end + end + + module SphincsVariant + SHAKE128S = 'shake128s' + SHAKE192S = 'shake192s' + SHAKE256S = 'shake256s' + + SIGNATURE_SIZES = { + SHAKE128S => 7856, + SHAKE192S => 16_224, + SHAKE256S => 29_792 + }.freeze + + SECURITY_LEVELS = { + SHAKE128S => 128, + SHAKE192S => 192, + SHAKE256S => 256 + }.freeze + + def self.signature_size(variant) + SIGNATURE_SIZES[variant] || 7856 + end + + def self.security_level(variant) + SECURITY_LEVELS[variant] || 128 + end + end + + module PqAlgorithm + DILITHIUM3 = 'dilithium3' + FALCON512 = 'falcon512' + FALCON1024 = 'falcon1024' + SPHINCS128S = 'sphincs128s' + SPHINCS192S = 'sphincs192s' + SPHINCS256S = 'sphincs256s' + end + + module AlgorithmFamily + CLASSICAL = 'classical' + LATTICE = 'lattice' + HASH_BASED = 'hash_based' + HYBRID = 'hybrid' + end + + # ============================================================================ + # Configuration Types + # ============================================================================ + + # Configuration for the Crypto SDK. + class CryptoConfig + attr_accessor :api_key, :endpoint, :timeout, :retries, :debug, :default_network + + def initialize(api_key:, endpoint: DEFAULT_ENDPOINT, timeout: 30_000, retries: 3, debug: false, default_network: Network::MAINNET) + @api_key = api_key + @endpoint = endpoint + @timeout = timeout + @retries = retries + @debug = debug + @default_network = default_network + end + end + + # Configuration for key derivation. + class DerivationConfig + attr_accessor :salt, :info, :output_length + + def initialize(salt: nil, info: nil, output_length: 32) + @salt = salt + @info = info + @output_length = output_length + end + end + + # Configuration for password-based key derivation. + class PasswordDerivationConfig + attr_accessor :salt, :iterations, :output_length + + def initialize(salt:, iterations: 100_000, output_length: 32) + @salt = salt + @iterations = iterations + @output_length = output_length + end + end + + # BIP-44 derivation path. + class DerivationPath + COIN_TYPE = 0x5359 + + attr_reader :account, :change, :index + + def initialize(account:, change:, index:) + @account = account + @change = change + @index = index + end + + def self.external(account:, index:) + new(account: account, change: 0, index: index) + end + + def self.internal(account:, index:) + new(account: account, change: 1, index: index) + end + + def to_s + "m/44'/#{COIN_TYPE}'/#{account}'/#{change}/#{index}" + end + + def to_h + { account: account, change: change, index: index } + end + end + + # ============================================================================ + # Key Types + # ============================================================================ + + # Hybrid public key combining Ed25519 and Dilithium3. + class HybridPublicKey + attr_accessor :ed25519, :dilithium + + def initialize(ed25519: nil, dilithium: nil) + @ed25519 = ed25519 + @dilithium = dilithium + end + + def ed25519_bytes + Base64.strict_decode64(@ed25519 || '') + end + + def dilithium_bytes + Base64.strict_decode64(@dilithium || '') + end + + def size + ed25519_bytes.bytesize + dilithium_bytes.bytesize + end + + def to_h + { 'ed25519' => ed25519, 'dilithium' => dilithium } + end + + def self.from_hash(hash) + new(ed25519: hash['ed25519'], dilithium: hash['dilithium']) + end + end + + # Secret key for hybrid signatures. + class SecretKey + attr_accessor :ed25519_seed, :master_seed + + def initialize(ed25519_seed: nil, master_seed: nil) + @ed25519_seed = ed25519_seed + @master_seed = master_seed + end + + def ed25519_seed_bytes + Base64.strict_decode64(@ed25519_seed || '') + end + + def master_seed_bytes + Base64.strict_decode64(@master_seed || '') + end + + def to_h + { 'ed25519_seed' => ed25519_seed, 'master_seed' => master_seed } + end + + def self.from_hash(hash) + new(ed25519_seed: hash['ed25519_seed'], master_seed: hash['master_seed']) + end + end + + # Falcon public key. + class FalconPublicKey + attr_accessor :variant, :bytes + + def initialize(variant: nil, bytes: nil) + @variant = variant + @bytes = bytes + end + + def key_bytes + Base64.strict_decode64(@bytes || '') + end + + def self.from_hash(hash) + new(variant: hash['variant'], bytes: hash['bytes']) + end + end + + # Falcon secret key. + class FalconSecretKey + attr_accessor :variant, :bytes + + def initialize(variant: nil, bytes: nil) + @variant = variant + @bytes = bytes + end + + def key_bytes + Base64.strict_decode64(@bytes || '') + end + + def self.from_hash(hash) + new(variant: hash['variant'], bytes: hash['bytes']) + end + end + + # SPHINCS+ public key. + class SphincsPublicKey + attr_accessor :variant, :bytes + + def initialize(variant: nil, bytes: nil) + @variant = variant + @bytes = bytes + end + + def key_bytes + Base64.strict_decode64(@bytes || '') + end + + def self.from_hash(hash) + new(variant: hash['variant'], bytes: hash['bytes']) + end + end + + # SPHINCS+ secret key. + class SphincsSecretKey + attr_accessor :variant, :bytes + + def initialize(variant: nil, bytes: nil) + @variant = variant + @bytes = bytes + end + + def key_bytes + Base64.strict_decode64(@bytes || '') + end + + def self.from_hash(hash) + new(variant: hash['variant'], bytes: hash['bytes']) + end + end + + # ============================================================================ + # Signature Types + # ============================================================================ + + # Hybrid signature combining Ed25519 and Dilithium3. + class HybridSignature + attr_accessor :ed25519, :dilithium + + def initialize(ed25519: nil, dilithium: nil) + @ed25519 = ed25519 + @dilithium = dilithium + end + + def ed25519_bytes + Base64.strict_decode64(@ed25519 || '') + end + + def dilithium_bytes + Base64.strict_decode64(@dilithium || '') + end + + def size + ed25519_bytes.bytesize + dilithium_bytes.bytesize + end + + def to_bytes + ed25519_bytes + dilithium_bytes + end + + def to_h + { 'ed25519' => ed25519, 'dilithium' => dilithium } + end + + def self.from_hash(hash) + new(ed25519: hash['ed25519'], dilithium: hash['dilithium']) + end + end + + # Falcon signature. + class FalconSignature + attr_accessor :variant, :signature + + def initialize(variant: nil, signature: nil) + @variant = variant + @signature = signature + end + + def signature_bytes + Base64.strict_decode64(@signature || '') + end + + def size + signature_bytes.bytesize + end + + def self.from_hash(hash) + new(variant: hash['variant'], signature: hash['signature']) + end + end + + # SPHINCS+ signature. + class SphincsSignature + attr_accessor :variant, :signature + + def initialize(variant: nil, signature: nil) + @variant = variant + @signature = signature + end + + def signature_bytes + Base64.strict_decode64(@signature || '') + end + + def size + signature_bytes.bytesize + end + + def self.from_hash(hash) + new(variant: hash['variant'], signature: hash['signature']) + end + end + + # ============================================================================ + # Keypair Types + # ============================================================================ + + # Hybrid keypair for Ed25519 + Dilithium3. + class HybridKeypair + attr_accessor :public_key, :secret_key, :addresses + + def initialize(public_key: nil, secret_key: nil, addresses: {}) + @public_key = public_key + @secret_key = secret_key + @addresses = addresses + end + + def get_address(network) + addresses[network] || '' + end + + def self.from_hash(hash) + new( + public_key: HybridPublicKey.from_hash(hash['public_key'] || {}), + secret_key: SecretKey.from_hash(hash['secret_key'] || {}), + addresses: hash['addresses'] || {} + ) + end + end + + # Falcon keypair. + class FalconKeypair + attr_accessor :variant, :public_key, :secret_key + + def initialize(variant: nil, public_key: nil, secret_key: nil) + @variant = variant + @public_key = public_key + @secret_key = secret_key + end + + def self.from_hash(hash) + new( + variant: hash['variant'], + public_key: FalconPublicKey.from_hash(hash['public_key'] || {}), + secret_key: FalconSecretKey.from_hash(hash['secret_key'] || {}) + ) + end + end + + # SPHINCS+ keypair. + class SphincsKeypair + attr_accessor :variant, :public_key, :secret_key + + def initialize(variant: nil, public_key: nil, secret_key: nil) + @variant = variant + @public_key = public_key + @secret_key = secret_key + end + + def self.from_hash(hash) + new( + variant: hash['variant'], + public_key: SphincsPublicKey.from_hash(hash['public_key'] || {}), + secret_key: SphincsSecretKey.from_hash(hash['secret_key'] || {}) + ) + end + end + + # ============================================================================ + # Mnemonic Types + # ============================================================================ + + # BIP-39 mnemonic phrase. + class Mnemonic + attr_accessor :phrase, :words, :word_count, :entropy + + def initialize(phrase: nil, words: nil, word_count: 0, entropy: nil) + @phrase = phrase + @words = words + @word_count = word_count + @entropy = entropy + end + + def get_words + words || phrase&.split(' ') || [] + end + + def get_word_count + word_count.positive? ? word_count : get_words.size + end + + def entropy_bytes + entropy ? Base64.strict_decode64(entropy) : '' + end + + def self.from_hash(hash) + new( + phrase: hash['phrase'], + words: hash['words'], + word_count: hash['word_count'] || 0, + entropy: hash['entropy'] + ) + end + end + + # Mnemonic validation result. + class MnemonicValidation + attr_accessor :valid, :error + + def initialize(valid: false, error: nil) + @valid = valid + @error = error + end + + def self.from_hash(hash) + new(valid: hash['valid'], error: hash['error']) + end + end + + # ============================================================================ + # Address Types + # ============================================================================ + + # Blockchain address. + class Address + attr_accessor :address, :network, :pubkey_hash + + def initialize(address: nil, network: nil, pubkey_hash: nil) + @address = address + @network = network + @pubkey_hash = pubkey_hash + end + + def pubkey_hash_bytes + pubkey_hash ? Base64.strict_decode64(pubkey_hash) : '' + end + + def self.from_hash(hash) + new( + address: hash['address'], + network: hash['network'], + pubkey_hash: hash['pubkey_hash'] + ) + end + end + + # ============================================================================ + # Hash Types + # ============================================================================ + + # 256-bit hash result. + class Hash256 + attr_accessor :hash + + def initialize(hash: nil) + @hash = hash + end + + def hex + hash + end + + def bytes + [hash].pack('H*') + end + + def self.from_hash(hash_data) + new(hash: hash_data['hash']) + end + end + + # ============================================================================ + # Error Types + # ============================================================================ + + # Crypto SDK exception. + class CryptoError < StandardError + attr_reader :code, :http_status + + def initialize(message, code: nil, http_status: nil) + super(message) + @code = code + @http_status = http_status + end + end + end +end diff --git a/sdk/rust/src/crypto/client.rs b/sdk/rust/src/crypto/client.rs new file mode 100644 index 0000000..9333858 --- /dev/null +++ b/sdk/rust/src/crypto/client.rs @@ -0,0 +1,673 @@ +//! Crypto SDK client implementation + +use super::types::*; +use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; +use reqwest::Client; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; + +/// Main Synor Crypto client +pub struct SynorCrypto { + config: CryptoConfig, + client: Client, + closed: Arc, +} + +impl SynorCrypto { + /// Creates a new client + pub fn new(config: CryptoConfig) -> Self { + let client = Client::builder() + .timeout(std::time::Duration::from_millis(config.timeout_ms)) + .build() + .expect("Failed to create HTTP client"); + + Self { + config, + client, + closed: Arc::new(AtomicBool::new(false)), + } + } + + /// Returns the default network + pub fn default_network(&self) -> Network { + self.config.default_network + } + + /// Returns the mnemonic client + pub fn mnemonic(&self) -> MnemonicClient { + MnemonicClient { crypto: self } + } + + /// Returns the keypair client + pub fn keypairs(&self) -> KeypairClient { + KeypairClient { crypto: self } + } + + /// Returns the signing client + pub fn signing(&self) -> SigningClient { + SigningClient { crypto: self } + } + + /// Returns the Falcon client + pub fn falcon(&self) -> FalconClient { + FalconClient { crypto: self } + } + + /// Returns the SPHINCS+ client + pub fn sphincs(&self) -> SphincsClient { + SphincsClient { crypto: self } + } + + /// Returns the KDF client + pub fn kdf(&self) -> KdfClient { + KdfClient { crypto: self } + } + + /// Returns the hash client + pub fn hash(&self) -> HashClient { + HashClient { crypto: self } + } + + /// Checks service health + pub async fn health_check(&self) -> Result { + #[derive(Deserialize)] + struct HealthResponse { + status: String, + } + + match self.get::("/health").await { + Ok(r) => Ok(r.status == "healthy"), + Err(_) => Ok(false), + } + } + + /// Closes the client + pub fn close(&self) { + self.closed.store(true, Ordering::SeqCst); + } + + /// Returns true if closed + pub fn is_closed(&self) -> bool { + self.closed.load(Ordering::SeqCst) + } + + async fn get Deserialize<'de>>(&self, path: &str) -> Result { + self.check_closed()?; + + let resp = self + .client + .get(format!("{}{}", self.config.endpoint, path)) + .header("Authorization", format!("Bearer {}", self.config.api_key)) + .header("X-SDK-Version", "rust/0.1.0") + .send() + .await + .map_err(|e| CryptoError::NetworkError(e.to_string()))?; + + self.handle_response(resp).await + } + + async fn post Deserialize<'de>, B: Serialize>( + &self, + path: &str, + body: &B, + ) -> Result { + self.check_closed()?; + + let resp = self + .client + .post(format!("{}{}", self.config.endpoint, path)) + .header("Authorization", format!("Bearer {}", self.config.api_key)) + .header("Content-Type", "application/json") + .header("X-SDK-Version", "rust/0.1.0") + .json(body) + .send() + .await + .map_err(|e| CryptoError::NetworkError(e.to_string()))?; + + self.handle_response(resp).await + } + + async fn handle_response Deserialize<'de>>( + &self, + resp: reqwest::Response, + ) -> Result { + let status = resp.status(); + let text = resp.text().await.map_err(|e| CryptoError::NetworkError(e.to_string()))?; + + if !status.is_success() { + #[derive(Deserialize)] + struct ErrorResponse { + message: Option, + code: Option, + } + + let err: ErrorResponse = serde_json::from_str(&text).unwrap_or(ErrorResponse { + message: Some(format!("HTTP {}", status)), + code: None, + }); + + return Err(CryptoError::ApiError { + message: err.message.unwrap_or_else(|| format!("HTTP {}", status)), + code: err.code, + }); + } + + serde_json::from_str(&text).map_err(|e| CryptoError::NetworkError(e.to_string())) + } + + fn check_closed(&self) -> Result<(), CryptoError> { + if self.is_closed() { + return Err(CryptoError::ClientClosed); + } + Ok(()) + } +} + +// ============================================================================ +// Mnemonic Client +// ============================================================================ + +/// Mnemonic operations +pub struct MnemonicClient<'a> { + crypto: &'a SynorCrypto, +} + +impl<'a> MnemonicClient<'a> { + /// Generates a new random mnemonic + pub async fn generate(&self, word_count: usize) -> Result { + #[derive(Serialize)] + struct Request { + word_count: usize, + } + + #[derive(Deserialize)] + struct Response { + phrase: String, + words: Vec, + word_count: usize, + entropy: Option, + } + + let resp: Response = self.crypto.post("/mnemonic/generate", &Request { word_count }).await?; + + Ok(Mnemonic { + phrase: resp.phrase, + words: resp.words, + word_count: resp.word_count, + entropy: resp.entropy.map(|e| BASE64.decode(e).unwrap_or_default()).unwrap_or_default(), + }) + } + + /// Creates a mnemonic from a phrase + pub async fn from_phrase(&self, phrase: &str) -> Result { + #[derive(Serialize)] + struct Request<'a> { + phrase: &'a str, + } + + #[derive(Deserialize)] + struct Response { + phrase: String, + words: Vec, + word_count: usize, + entropy: Option, + } + + let resp: Response = self.crypto.post("/mnemonic/from-phrase", &Request { phrase }).await?; + + Ok(Mnemonic { + phrase: resp.phrase, + words: resp.words, + word_count: resp.word_count, + entropy: resp.entropy.map(|e| BASE64.decode(e).unwrap_or_default()).unwrap_or_default(), + }) + } + + /// Validates a mnemonic phrase + pub async fn validate(&self, phrase: &str) -> Result { + #[derive(Serialize)] + struct Request<'a> { + phrase: &'a str, + } + + #[derive(Deserialize)] + struct Response { + valid: bool, + error: Option, + } + + let resp: Response = self.crypto.post("/mnemonic/validate", &Request { phrase }).await?; + + Ok(MnemonicValidation { + valid: resp.valid, + error: resp.error, + }) + } + + /// Derives a seed from a mnemonic + pub async fn to_seed(&self, phrase: &str, passphrase: &str) -> Result, CryptoError> { + #[derive(Serialize)] + struct Request<'a> { + phrase: &'a str, + passphrase: &'a str, + } + + #[derive(Deserialize)] + struct Response { + seed: String, + } + + let resp: Response = self.crypto.post("/mnemonic/to-seed", &Request { phrase, passphrase }).await?; + + BASE64.decode(resp.seed).map_err(|e| CryptoError::DerivationFailed(e.to_string())) + } +} + +// ============================================================================ +// Keypair Client +// ============================================================================ + +/// Keypair operations +pub struct KeypairClient<'a> { + crypto: &'a SynorCrypto, +} + +impl<'a> KeypairClient<'a> { + /// Generates a new random keypair + pub async fn generate(&self) -> Result { + let resp: KeypairResponse = self.crypto.post("/keypair/generate", &()).await?; + Ok(resp.into_keypair()) + } + + /// Creates a keypair from a mnemonic + pub async fn from_mnemonic(&self, phrase: &str, passphrase: &str) -> Result { + #[derive(Serialize)] + struct Request<'a> { + phrase: &'a str, + passphrase: &'a str, + } + + let resp: KeypairResponse = self.crypto.post("/keypair/from-mnemonic", &Request { phrase, passphrase }).await?; + Ok(resp.into_keypair()) + } + + /// Creates a keypair from a seed + pub async fn from_seed(&self, seed: &[u8]) -> Result { + #[derive(Serialize)] + struct Request { + seed: String, + } + + let resp: KeypairResponse = self.crypto.post("/keypair/from-seed", &Request { + seed: BASE64.encode(seed), + }).await?; + Ok(resp.into_keypair()) + } +} + +#[derive(Deserialize)] +struct KeypairResponse { + public_key: PublicKeyResponse, + secret_key: SecretKeyResponse, + addresses: HashMap, +} + +#[derive(Deserialize)] +struct PublicKeyResponse { + ed25519: String, + dilithium: String, +} + +#[derive(Deserialize)] +struct SecretKeyResponse { + ed25519_seed: String, + master_seed: String, +} + +impl KeypairResponse { + fn into_keypair(self) -> HybridKeypair { + let ed25519 = BASE64.decode(&self.public_key.ed25519).unwrap_or_default(); + let dilithium = BASE64.decode(&self.public_key.dilithium).unwrap_or_default(); + let ed25519_seed = BASE64.decode(&self.secret_key.ed25519_seed).unwrap_or_default(); + let master_seed = BASE64.decode(&self.secret_key.master_seed).unwrap_or_default(); + + let mut addresses = HashMap::new(); + for (k, v) in self.addresses { + if let Ok(network) = serde_json::from_str::(&format!("\"{}\"", k)) { + addresses.insert(network, v); + } + } + + HybridKeypair::new( + HybridPublicKey { ed25519, dilithium }, + SecretKey { ed25519_seed, master_seed }, + addresses, + ) + } +} + +// ============================================================================ +// Signing Client +// ============================================================================ + +/// Signing operations +pub struct SigningClient<'a> { + crypto: &'a SynorCrypto, +} + +impl<'a> SigningClient<'a> { + /// Signs a message with a hybrid keypair + pub async fn sign(&self, keypair: &HybridKeypair, message: &[u8]) -> Result { + #[derive(Serialize)] + struct Request { + secret_key: SecretKeyRequest, + message: String, + } + + #[derive(Serialize)] + struct SecretKeyRequest { + ed25519_seed: String, + master_seed: String, + } + + #[derive(Deserialize)] + struct Response { + ed25519: String, + dilithium: String, + } + + let resp: Response = self.crypto.post("/sign/hybrid", &Request { + secret_key: SecretKeyRequest { + ed25519_seed: BASE64.encode(&keypair.secret_key.ed25519_seed), + master_seed: BASE64.encode(&keypair.secret_key.master_seed), + }, + message: BASE64.encode(message), + }).await?; + + Ok(HybridSignature { + ed25519: BASE64.decode(resp.ed25519).unwrap_or_default(), + dilithium: BASE64.decode(resp.dilithium).unwrap_or_default(), + }) + } + + /// Verifies a hybrid signature + pub async fn verify( + &self, + public_key: &HybridPublicKey, + message: &[u8], + signature: &HybridSignature, + ) -> Result { + #[derive(Serialize)] + struct Request { + public_key: PublicKeyRequest, + message: String, + signature: SignatureRequest, + } + + #[derive(Serialize)] + struct PublicKeyRequest { + ed25519: String, + dilithium: String, + } + + #[derive(Serialize)] + struct SignatureRequest { + ed25519: String, + dilithium: String, + } + + #[derive(Deserialize)] + struct Response { + valid: bool, + } + + let resp: Response = self.crypto.post("/sign/verify", &Request { + public_key: PublicKeyRequest { + ed25519: BASE64.encode(&public_key.ed25519), + dilithium: BASE64.encode(&public_key.dilithium), + }, + message: BASE64.encode(message), + signature: SignatureRequest { + ed25519: BASE64.encode(&signature.ed25519), + dilithium: BASE64.encode(&signature.dilithium), + }, + }).await?; + + Ok(resp.valid) + } +} + +// ============================================================================ +// Falcon Client +// ============================================================================ + +/// Falcon operations +pub struct FalconClient<'a> { + crypto: &'a SynorCrypto, +} + +impl<'a> FalconClient<'a> { + /// Generates a Falcon keypair + pub async fn generate(&self, variant: FalconVariant) -> Result { + #[derive(Serialize)] + struct Request { + variant: FalconVariant, + } + + #[derive(Deserialize)] + struct Response { + variant: FalconVariant, + public_key: KeyBytes, + secret_key: KeyBytes, + } + + #[derive(Deserialize)] + struct KeyBytes { + bytes: String, + } + + let resp: Response = self.crypto.post("/falcon/generate", &Request { variant }).await?; + + Ok(FalconKeypair { + variant: resp.variant, + public_key: FalconPublicKey { + variant: resp.variant, + bytes: BASE64.decode(resp.public_key.bytes).unwrap_or_default(), + }, + secret_key: FalconSecretKey { + variant: resp.variant, + bytes: BASE64.decode(resp.secret_key.bytes).unwrap_or_default(), + }, + }) + } + + /// Signs with Falcon + pub async fn sign(&self, keypair: &FalconKeypair, message: &[u8]) -> Result { + #[derive(Serialize)] + struct Request { + variant: FalconVariant, + secret_key: String, + message: String, + } + + #[derive(Deserialize)] + struct Response { + signature: String, + variant: FalconVariant, + } + + let resp: Response = self.crypto.post("/falcon/sign", &Request { + variant: keypair.variant, + secret_key: BASE64.encode(&keypair.secret_key.bytes), + message: BASE64.encode(message), + }).await?; + + Ok(FalconSignature { + variant: resp.variant, + bytes: BASE64.decode(resp.signature).unwrap_or_default(), + }) + } +} + +// ============================================================================ +// SPHINCS+ Client +// ============================================================================ + +/// SPHINCS+ operations +pub struct SphincsClient<'a> { + crypto: &'a SynorCrypto, +} + +impl<'a> SphincsClient<'a> { + /// Generates a SPHINCS+ keypair + pub async fn generate(&self, variant: SphincsVariant) -> Result { + #[derive(Serialize)] + struct Request { + variant: SphincsVariant, + } + + #[derive(Deserialize)] + struct Response { + variant: SphincsVariant, + public_key: KeyBytes, + secret_key: KeyBytes, + } + + #[derive(Deserialize)] + struct KeyBytes { + bytes: String, + } + + let resp: Response = self.crypto.post("/sphincs/generate", &Request { variant }).await?; + + Ok(SphincsKeypair { + variant: resp.variant, + public_key: SphincsPublicKey { + variant: resp.variant, + bytes: BASE64.decode(resp.public_key.bytes).unwrap_or_default(), + }, + secret_key: SphincsSecretKey { + variant: resp.variant, + bytes: BASE64.decode(resp.secret_key.bytes).unwrap_or_default(), + }, + }) + } +} + +// ============================================================================ +// KDF Client +// ============================================================================ + +/// Key derivation operations +pub struct KdfClient<'a> { + crypto: &'a SynorCrypto, +} + +impl<'a> KdfClient<'a> { + /// Derives a key using HKDF + pub async fn derive_key(&self, seed: &[u8], config: &DerivationConfig) -> Result, CryptoError> { + #[derive(Serialize)] + struct Request { + seed: String, + salt: Option, + info: Option, + output_length: usize, + } + + #[derive(Deserialize)] + struct Response { + key: String, + } + + let resp: Response = self.crypto.post("/kdf/hkdf", &Request { + seed: BASE64.encode(seed), + salt: config.salt.as_ref().map(|s| BASE64.encode(s)), + info: config.info.as_ref().map(|i| BASE64.encode(i)), + output_length: config.output_length, + }).await?; + + BASE64.decode(resp.key).map_err(|e| CryptoError::DerivationFailed(e.to_string())) + } + + /// Derives a key from a password + pub async fn derive_from_password( + &self, + password: &[u8], + config: &PasswordDerivationConfig, + ) -> Result, CryptoError> { + #[derive(Serialize)] + struct Request { + password: String, + salt: String, + iterations: u32, + output_length: usize, + } + + #[derive(Deserialize)] + struct Response { + key: String, + } + + let resp: Response = self.crypto.post("/kdf/pbkdf2", &Request { + password: BASE64.encode(password), + salt: BASE64.encode(&config.salt), + iterations: config.iterations, + output_length: config.output_length, + }).await?; + + BASE64.decode(resp.key).map_err(|e| CryptoError::DerivationFailed(e.to_string())) + } +} + +// ============================================================================ +// Hash Client +// ============================================================================ + +/// Hashing operations +pub struct HashClient<'a> { + crypto: &'a SynorCrypto, +} + +impl<'a> HashClient<'a> { + /// Computes SHA3-256 hash + pub async fn sha3_256(&self, data: &[u8]) -> Result { + #[derive(Serialize)] + struct Request { + data: String, + } + + #[derive(Deserialize)] + struct Response { + hash: String, + } + + let resp: Response = self.crypto.post("/hash/sha3-256", &Request { + data: BASE64.encode(data), + }).await?; + + let bytes = hex::decode(&resp.hash).unwrap_or_default(); + Ok(Hash256 { bytes, hex: resp.hash }) + } + + /// Computes BLAKE3 hash + pub async fn blake3(&self, data: &[u8]) -> Result { + #[derive(Serialize)] + struct Request { + data: String, + } + + #[derive(Deserialize)] + struct Response { + hash: String, + } + + let resp: Response = self.crypto.post("/hash/blake3", &Request { + data: BASE64.encode(data), + }).await?; + + let bytes = hex::decode(&resp.hash).unwrap_or_default(); + Ok(Hash256 { bytes, hex: resp.hash }) + } +} diff --git a/sdk/rust/src/crypto/mod.rs b/sdk/rust/src/crypto/mod.rs new file mode 100644 index 0000000..bad52b8 --- /dev/null +++ b/sdk/rust/src/crypto/mod.rs @@ -0,0 +1,42 @@ +//! Synor Crypto SDK for Rust +//! +//! Quantum-resistant cryptographic primitives for the Synor blockchain. +//! +//! # Features +//! +//! - Hybrid Ed25519 + Dilithium3 signatures (quantum-resistant) +//! - BIP-39 mnemonic support (12-24 words) +//! - BIP-44 hierarchical key derivation +//! - Post-quantum algorithms: Falcon, SPHINCS+ +//! - Secure key derivation (HKDF, PBKDF2) +//! +//! # Example +//! +//! ```rust,no_run +//! use synor_crypto::{SynorCrypto, CryptoConfig, Network}; +//! +//! #[tokio::main] +//! async fn main() -> Result<(), Box> { +//! let config = CryptoConfig::new("your-api-key"); +//! let crypto = SynorCrypto::new(config); +//! +//! // Generate a mnemonic +//! let mnemonic = crypto.mnemonic().generate(24).await?; +//! println!("Backup words: {}", mnemonic.phrase); +//! +//! // Create keypair from mnemonic +//! let keypair = crypto.keypairs().from_mnemonic(&mnemonic.phrase, "").await?; +//! let address = keypair.address(Network::Mainnet); +//! +//! // Sign a message +//! let signature = crypto.signing().sign(&keypair, b"Hello!").await?; +//! +//! Ok(()) +//! } +//! ``` + +mod types; +mod client; + +pub use types::*; +pub use client::*; diff --git a/sdk/rust/src/crypto/types.rs b/sdk/rust/src/crypto/types.rs new file mode 100644 index 0000000..21f7226 --- /dev/null +++ b/sdk/rust/src/crypto/types.rs @@ -0,0 +1,537 @@ +//! Crypto SDK types for Rust + +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use thiserror::Error; + +// ============================================================================ +// Enumerations +// ============================================================================ + +/// Network type for address generation +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum Network { + Mainnet, + Testnet, + Devnet, +} + +impl Default for Network { + fn default() -> Self { + Network::Mainnet + } +} + +/// Falcon variant selection +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum FalconVariant { + Falcon512, + Falcon1024, +} + +impl Default for FalconVariant { + fn default() -> Self { + FalconVariant::Falcon512 + } +} + +impl FalconVariant { + /// Returns the signature size for this variant + pub const fn signature_size(&self) -> usize { + match self { + FalconVariant::Falcon512 => 690, + FalconVariant::Falcon1024 => 1330, + } + } + + /// Returns the public key size for this variant + pub const fn public_key_size(&self) -> usize { + match self { + FalconVariant::Falcon512 => 897, + FalconVariant::Falcon1024 => 1793, + } + } + + /// Returns the security level in bits + pub const fn security_level(&self) -> u16 { + match self { + FalconVariant::Falcon512 => 128, + FalconVariant::Falcon1024 => 256, + } + } +} + +/// SPHINCS+ variant selection +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum SphincsVariant { + Shake128s, + Shake192s, + Shake256s, +} + +impl Default for SphincsVariant { + fn default() -> Self { + SphincsVariant::Shake128s + } +} + +impl SphincsVariant { + /// Returns the signature size for this variant + pub const fn signature_size(&self) -> usize { + match self { + SphincsVariant::Shake128s => 7856, + SphincsVariant::Shake192s => 16224, + SphincsVariant::Shake256s => 29792, + } + } + + /// Returns the security level in bits + pub const fn security_level(&self) -> u16 { + match self { + SphincsVariant::Shake128s => 128, + SphincsVariant::Shake192s => 192, + SphincsVariant::Shake256s => 256, + } + } +} + +/// Post-quantum algorithm selection +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum PqAlgorithm { + Dilithium3, + Falcon512, + Falcon1024, + Sphincs128s, + Sphincs192s, + Sphincs256s, +} + +/// Algorithm family classification +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum AlgorithmFamily { + Classical, + Lattice, + HashBased, + Hybrid, +} + +// ============================================================================ +// Configuration Types +// ============================================================================ + +/// Crypto SDK configuration +#[derive(Debug, Clone)] +pub struct CryptoConfig { + pub api_key: String, + pub endpoint: String, + pub timeout_ms: u64, + pub retries: u32, + pub debug: bool, + pub default_network: Network, +} + +impl CryptoConfig { + /// Creates a new config with the given API key + pub fn new(api_key: impl Into) -> Self { + Self { + api_key: api_key.into(), + endpoint: "https://crypto.synor.io/v1".to_string(), + timeout_ms: 30000, + retries: 3, + debug: false, + default_network: Network::Mainnet, + } + } + + /// Sets the endpoint + pub fn with_endpoint(mut self, endpoint: impl Into) -> Self { + self.endpoint = endpoint.into(); + self + } + + /// Sets the timeout + pub fn with_timeout(mut self, timeout_ms: u64) -> Self { + self.timeout_ms = timeout_ms; + self + } +} + +/// Key derivation configuration +#[derive(Debug, Clone, Default)] +pub struct DerivationConfig { + pub salt: Option>, + pub info: Option>, + pub output_length: usize, +} + +/// Password derivation configuration +#[derive(Debug, Clone)] +pub struct PasswordDerivationConfig { + pub salt: Vec, + pub iterations: u32, + pub output_length: usize, +} + +/// BIP-44 derivation path +#[derive(Debug, Clone, Default)] +pub struct DerivationPath { + pub account: u32, + pub change: u32, + pub index: u32, +} + +impl DerivationPath { + /// Synor coin type for BIP-44 + pub const COIN_TYPE: u32 = 0x5359; + + /// Creates a new derivation path + pub fn new(account: u32, change: u32, index: u32) -> Self { + Self { account, change, index } + } + + /// Creates a path for external addresses + pub fn external(account: u32, index: u32) -> Self { + Self { account, change: 0, index } + } + + /// Creates a path for internal (change) addresses + pub fn internal(account: u32, index: u32) -> Self { + Self { account, change: 1, index } + } +} + +impl std::fmt::Display for DerivationPath { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "m/44'/{}'/{}'/{}/{}", + Self::COIN_TYPE, + self.account, + self.change, + self.index + ) + } +} + +// ============================================================================ +// Key Types +// ============================================================================ + +/// Hybrid public key (Ed25519 + Dilithium3) +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HybridPublicKey { + /// Ed25519 component (32 bytes) + pub ed25519: Vec, + /// Dilithium3 component (~1952 bytes) + pub dilithium: Vec, +} + +impl HybridPublicKey { + /// Returns the total size in bytes + pub fn size(&self) -> usize { + self.ed25519.len() + self.dilithium.len() + } +} + +/// Secret key (master seed) +#[derive(Debug, Clone)] +pub struct SecretKey { + /// Ed25519 seed (32 bytes) + pub ed25519_seed: Vec, + /// Master seed (64 bytes) + pub master_seed: Vec, +} + +/// Falcon public key +#[derive(Debug, Clone)] +pub struct FalconPublicKey { + pub variant: FalconVariant, + pub bytes: Vec, +} + +/// Falcon secret key +#[derive(Debug, Clone)] +pub struct FalconSecretKey { + pub variant: FalconVariant, + pub bytes: Vec, +} + +/// SPHINCS+ public key +#[derive(Debug, Clone)] +pub struct SphincsPublicKey { + pub variant: SphincsVariant, + pub bytes: Vec, +} + +/// SPHINCS+ secret key +#[derive(Debug, Clone)] +pub struct SphincsSecretKey { + pub variant: SphincsVariant, + pub bytes: Vec, +} + +// ============================================================================ +// Signature Types +// ============================================================================ + +/// Hybrid signature (Ed25519 + Dilithium3) +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HybridSignature { + /// Ed25519 component (64 bytes) + pub ed25519: Vec, + /// Dilithium3 component (~3293 bytes) + pub dilithium: Vec, +} + +impl HybridSignature { + /// Returns the total size in bytes + pub fn size(&self) -> usize { + self.ed25519.len() + self.dilithium.len() + } + + /// Serializes the signature to bytes + pub fn to_bytes(&self) -> Vec { + let mut result = Vec::with_capacity(self.size()); + result.extend_from_slice(&self.ed25519); + result.extend_from_slice(&self.dilithium); + result + } + + /// Deserializes a signature from bytes + pub fn from_bytes(data: &[u8]) -> Result { + if data.len() < 64 { + return Err(CryptoError::InvalidSignature("Too short".to_string())); + } + Ok(Self { + ed25519: data[..64].to_vec(), + dilithium: data[64..].to_vec(), + }) + } +} + +/// Falcon signature +#[derive(Debug, Clone)] +pub struct FalconSignature { + pub variant: FalconVariant, + pub bytes: Vec, +} + +/// SPHINCS+ signature +#[derive(Debug, Clone)] +pub struct SphincsSignature { + pub variant: SphincsVariant, + pub bytes: Vec, +} + +// ============================================================================ +// Keypair Types +// ============================================================================ + +/// Hybrid keypair (Ed25519 + Dilithium3) +#[derive(Debug, Clone)] +pub struct HybridKeypair { + pub public_key: HybridPublicKey, + pub secret_key: SecretKey, + addresses: HashMap, +} + +impl HybridKeypair { + /// Returns the address for a network + pub fn address(&self, network: Network) -> Option<&str> { + self.addresses.get(&network).map(|s| s.as_str()) + } + + /// Creates a new keypair with addresses + pub fn new( + public_key: HybridPublicKey, + secret_key: SecretKey, + addresses: HashMap, + ) -> Self { + Self { public_key, secret_key, addresses } + } +} + +/// Falcon keypair +#[derive(Debug, Clone)] +pub struct FalconKeypair { + pub variant: FalconVariant, + pub public_key: FalconPublicKey, + pub secret_key: FalconSecretKey, +} + +/// SPHINCS+ keypair +#[derive(Debug, Clone)] +pub struct SphincsKeypair { + pub variant: SphincsVariant, + pub public_key: SphincsPublicKey, + pub secret_key: SphincsSecretKey, +} + +// ============================================================================ +// Mnemonic Types +// ============================================================================ + +/// BIP-39 mnemonic phrase +#[derive(Debug, Clone)] +pub struct Mnemonic { + pub phrase: String, + pub words: Vec, + pub word_count: usize, + pub entropy: Vec, +} + +/// Mnemonic validation result +#[derive(Debug, Clone)] +pub struct MnemonicValidation { + pub valid: bool, + pub error: Option, +} + +// ============================================================================ +// Address Types +// ============================================================================ + +/// Blockchain address +#[derive(Debug, Clone)] +pub struct Address { + pub address: String, + pub network: Network, + pub pubkey_hash: Vec, +} + +// ============================================================================ +// Hash Types +// ============================================================================ + +/// 256-bit hash +#[derive(Debug, Clone)] +pub struct Hash256 { + pub bytes: Vec, + pub hex: String, +} + +impl Hash256 { + /// Creates a hash from bytes + pub fn new(bytes: Vec) -> Self { + let hex = hex::encode(&bytes); + Self { bytes, hex } + } +} + +// ============================================================================ +// Negotiation Types +// ============================================================================ + +/// Algorithm capabilities for negotiation +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AlgorithmCapabilities { + pub pq_algorithms: Vec, + pub classical: bool, + pub hybrid: bool, + pub preferred: Option, +} + +/// Negotiation policy +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NegotiationPolicy { + pub min_security_level: u16, + pub prefer_compact: bool, + pub allow_classical: bool, + pub required_families: Vec, +} + +impl Default for NegotiationPolicy { + fn default() -> Self { + Self { + min_security_level: 128, + prefer_compact: false, + allow_classical: false, + required_families: vec![], + } + } +} + +/// Negotiation result +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NegotiationResult { + pub algorithm: PqAlgorithm, + pub security_level: u16, + pub family: AlgorithmFamily, + pub hybrid: bool, +} + +/// Session parameters after negotiation +#[derive(Debug, Clone)] +pub struct SessionParams { + pub algorithm: PqAlgorithm, + pub session_key: Option>, + pub expires_at: Option, +} + +// ============================================================================ +// Error Types +// ============================================================================ + +/// Crypto operation error +#[derive(Debug, Error)] +pub enum CryptoError { + #[error("Invalid mnemonic: {0}")] + InvalidMnemonic(String), + + #[error("Invalid key: {0}")] + InvalidKey(String), + + #[error("Invalid signature: {0}")] + InvalidSignature(String), + + #[error("Verification failed")] + VerificationFailed, + + #[error("Derivation failed: {0}")] + DerivationFailed(String), + + #[error("Algorithm mismatch")] + AlgorithmMismatch, + + #[error("Negotiation failed: {0}")] + NegotiationFailed(String), + + #[error("Network error: {0}")] + NetworkError(String), + + #[error("API error: {message} (code: {code:?})")] + ApiError { message: String, code: Option }, + + #[error("Client closed")] + ClientClosed, +} + +// ============================================================================ +// Constants +// ============================================================================ + +/// Ed25519 public key size +pub const ED25519_PUBLIC_KEY_SIZE: usize = 32; +/// Ed25519 secret key size +pub const ED25519_SECRET_KEY_SIZE: usize = 32; +/// Ed25519 signature size +pub const ED25519_SIGNATURE_SIZE: usize = 64; +/// Dilithium3 public key size +pub const DILITHIUM3_PUBLIC_KEY_SIZE: usize = 1952; +/// Dilithium3 signature size +pub const DILITHIUM3_SIGNATURE_SIZE: usize = 3293; +/// Hybrid signature size +pub const HYBRID_SIGNATURE_SIZE: usize = ED25519_SIGNATURE_SIZE + DILITHIUM3_SIGNATURE_SIZE; +/// BIP-44 coin type for Synor +pub const COIN_TYPE: u32 = 0x5359; +/// Minimum PBKDF2 iterations +pub const MIN_PBKDF2_ITERATIONS: u32 = 10000; +/// Minimum salt length +pub const MIN_SALT_LENGTH: usize = 8; +/// Default API endpoint +pub const DEFAULT_ENDPOINT: &str = "https://crypto.synor.io/v1"; diff --git a/sdk/swift/Sources/SynorCrypto/SynorCrypto.swift b/sdk/swift/Sources/SynorCrypto/SynorCrypto.swift new file mode 100644 index 0000000..643f3da --- /dev/null +++ b/sdk/swift/Sources/SynorCrypto/SynorCrypto.swift @@ -0,0 +1,347 @@ +import Foundation + +/// Synor Crypto SDK for Swift +/// +/// Quantum-resistant cryptographic primitives for the Synor blockchain. +/// +/// ```swift +/// let config = CryptoConfig(apiKey: "your-api-key") +/// let crypto = SynorCrypto(config: config) +/// +/// // Generate a mnemonic +/// let mnemonic = try await crypto.mnemonic.generate(wordCount: 24) +/// print("Backup words: \(mnemonic.phrase)") +/// +/// // Create keypair from mnemonic +/// let keypair = try await crypto.keypairs.fromMnemonic(phrase: mnemonic.phrase, passphrase: "") +/// let address = keypair.address(for: .mainnet) +/// +/// // Sign a message +/// let signature = try await crypto.signing.sign(keypair: keypair, message: "Hello!".data(using: .utf8)!) +/// ``` +public class SynorCrypto { + private let config: CryptoConfig + private let session: URLSession + private let encoder: JSONEncoder + private let decoder: JSONDecoder + private var _closed = false + + public let mnemonic: MnemonicClient + public let keypairs: KeypairClient + public let signing: SigningClient + public let falcon: FalconClient + public let sphincs: SphincsClient + public let kdf: KdfClient + public let hash: HashClient + + public var defaultNetwork: Network { config.defaultNetwork } + public var isClosed: Bool { _closed } + + public init(config: CryptoConfig) { + self.config = config + + let sessionConfig = URLSessionConfiguration.default + sessionConfig.timeoutIntervalForRequest = config.timeout + self.session = URLSession(configuration: sessionConfig) + + self.encoder = JSONEncoder() + encoder.keyEncodingStrategy = .convertToSnakeCase + + self.decoder = JSONDecoder() + decoder.keyDecodingStrategy = .convertFromSnakeCase + + self.mnemonic = MnemonicClient() + self.keypairs = KeypairClient() + self.signing = SigningClient() + self.falcon = FalconClient() + self.sphincs = SphincsClient() + self.kdf = KdfClient() + self.hash = HashClient() + + // Set parent references + self.mnemonic.crypto = self + self.keypairs.crypto = self + self.signing.crypto = self + self.falcon.crypto = self + self.sphincs.crypto = self + self.kdf.crypto = self + self.hash.crypto = self + } + + public func healthCheck() async -> Bool { + do { + let result: [String: String] = try await get(path: "/health") + return result["status"] == "healthy" + } catch { + return false + } + } + + public func getInfo() async throws -> [String: Any] { + try await get(path: "/info") + } + + public func close() { + _closed = true + session.invalidateAndCancel() + } + + // MARK: - Internal HTTP Methods + + func get(path: String) async throws -> T { + guard !_closed else { + throw CryptoError(message: "Client has been closed", code: "CLIENT_CLOSED") + } + + guard let url = URL(string: config.endpoint + path) else { + throw CryptoError(message: "Invalid URL", code: "INVALID_URL") + } + + var request = URLRequest(url: url) + request.httpMethod = "GET" + request.setValue("Bearer \(config.apiKey)", forHTTPHeaderField: "Authorization") + request.setValue("swift/0.1.0", forHTTPHeaderField: "X-SDK-Version") + + let (data, response) = try await session.data(for: request) + return try handleResponse(data: data, response: response) + } + + func post(path: String, body: [String: Any]? = nil) async throws -> T { + guard !_closed else { + throw CryptoError(message: "Client has been closed", code: "CLIENT_CLOSED") + } + + guard let url = URL(string: config.endpoint + path) else { + throw CryptoError(message: "Invalid URL", code: "INVALID_URL") + } + + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.setValue("Bearer \(config.apiKey)", forHTTPHeaderField: "Authorization") + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + request.setValue("swift/0.1.0", forHTTPHeaderField: "X-SDK-Version") + + if let body = body { + request.httpBody = try JSONSerialization.data(withJSONObject: body) + } else { + request.httpBody = "{}".data(using: .utf8) + } + + let (data, response) = try await session.data(for: request) + return try handleResponse(data: data, response: response) + } + + private func handleResponse(data: Data, response: URLResponse) throws -> T { + guard let httpResponse = response as? HTTPURLResponse else { + throw CryptoError(message: "Invalid response", code: "INVALID_RESPONSE") + } + + if httpResponse.statusCode >= 400 { + if let errorDict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] { + let message = errorDict["message"] as? String ?? "HTTP \(httpResponse.statusCode)" + let code = errorDict["code"] as? String + throw CryptoError(message: message, code: code) + } + throw CryptoError(message: "HTTP \(httpResponse.statusCode)", code: nil) + } + + return try decoder.decode(T.self, from: data) + } + + // MARK: - Sub-Clients + + public class MnemonicClient { + weak var crypto: SynorCrypto? + + public func generate(wordCount: Int = 24) async throws -> Mnemonic { + try await crypto!.post(path: "/mnemonic/generate", body: ["word_count": wordCount]) + } + + public func fromPhrase(phrase: String) async throws -> Mnemonic { + try await crypto!.post(path: "/mnemonic/from-phrase", body: ["phrase": phrase]) + } + + public func validate(phrase: String) async throws -> MnemonicValidation { + try await crypto!.post(path: "/mnemonic/validate", body: ["phrase": phrase]) + } + + public func toSeed(phrase: String, passphrase: String = "") async throws -> Data { + struct SeedResponse: Decodable { let seed: String } + let result: SeedResponse = try await crypto!.post( + path: "/mnemonic/to-seed", + body: ["phrase": phrase, "passphrase": passphrase] + ) + return Data(base64Encoded: result.seed) ?? Data() + } + + public func suggestWords(partial: String, limit: Int = 5) async throws -> [String] { + struct SuggestResponse: Decodable { let suggestions: [String] } + let result: SuggestResponse = try await crypto!.post( + path: "/mnemonic/suggest", + body: ["partial": partial, "limit": limit] + ) + return result.suggestions + } + } + + public class KeypairClient { + weak var crypto: SynorCrypto? + + public func generate() async throws -> HybridKeypair { + try await crypto!.post(path: "/keypair/generate") + } + + public func fromMnemonic(phrase: String, passphrase: String = "") async throws -> HybridKeypair { + try await crypto!.post( + path: "/keypair/from-mnemonic", + body: ["phrase": phrase, "passphrase": passphrase] + ) + } + + public func fromSeed(seed: Data) async throws -> HybridKeypair { + try await crypto!.post( + path: "/keypair/from-seed", + body: ["seed": seed.base64EncodedString()] + ) + } + + public func getAddress(publicKey: HybridPublicKey, network: Network) async throws -> Address { + try await crypto!.post( + path: "/keypair/address", + body: ["public_key": publicKey.toDict(), "network": network.rawValue] + ) + } + } + + public class SigningClient { + weak var crypto: SynorCrypto? + + public func sign(keypair: HybridKeypair, message: Data) async throws -> HybridSignature { + try await crypto!.post(path: "/sign/hybrid", body: [ + "secret_key": keypair.secretKey.toDict(), + "message": message.base64EncodedString() + ]) + } + + public func verify(publicKey: HybridPublicKey, message: Data, signature: HybridSignature) async throws -> Bool { + struct VerifyResponse: Decodable { let valid: Bool } + let result: VerifyResponse = try await crypto!.post(path: "/sign/verify", body: [ + "public_key": publicKey.toDict(), + "message": message.base64EncodedString(), + "signature": signature.toDict() + ]) + return result.valid + } + + public func signEd25519(secretKey: Data, message: Data) async throws -> Data { + struct SignResponse: Decodable { let signature: String } + let result: SignResponse = try await crypto!.post(path: "/sign/ed25519", body: [ + "secret_key": secretKey.base64EncodedString(), + "message": message.base64EncodedString() + ]) + return Data(base64Encoded: result.signature) ?? Data() + } + } + + public class FalconClient { + weak var crypto: SynorCrypto? + + public func generate(variant: FalconVariant = .falcon512) async throws -> FalconKeypair { + try await crypto!.post(path: "/falcon/generate", body: ["variant": variant.rawValue]) + } + + public func sign(keypair: FalconKeypair, message: Data) async throws -> FalconSignature { + try await crypto!.post(path: "/falcon/sign", body: [ + "variant": keypair.variantEnum.rawValue, + "secret_key": keypair.secretKey.keyBytes.base64EncodedString(), + "message": message.base64EncodedString() + ]) + } + + public func verify(publicKey: Data, message: Data, signature: FalconSignature) async throws -> Bool { + struct VerifyResponse: Decodable { let valid: Bool } + let result: VerifyResponse = try await crypto!.post(path: "/falcon/verify", body: [ + "variant": signature.variantEnum.rawValue, + "public_key": publicKey.base64EncodedString(), + "message": message.base64EncodedString(), + "signature": signature.signatureBytes.base64EncodedString() + ]) + return result.valid + } + } + + public class SphincsClient { + weak var crypto: SynorCrypto? + + public func generate(variant: SphincsVariant = .shake128s) async throws -> SphincsKeypair { + try await crypto!.post(path: "/sphincs/generate", body: ["variant": variant.rawValue]) + } + + public func sign(keypair: SphincsKeypair, message: Data) async throws -> SphincsSignature { + try await crypto!.post(path: "/sphincs/sign", body: [ + "variant": keypair.variantEnum.rawValue, + "secret_key": keypair.secretKey.keyBytes.base64EncodedString(), + "message": message.base64EncodedString() + ]) + } + + public func verify(publicKey: Data, message: Data, signature: SphincsSignature) async throws -> Bool { + struct VerifyResponse: Decodable { let valid: Bool } + let result: VerifyResponse = try await crypto!.post(path: "/sphincs/verify", body: [ + "variant": signature.variantEnum.rawValue, + "public_key": publicKey.base64EncodedString(), + "message": message.base64EncodedString(), + "signature": signature.signatureBytes.base64EncodedString() + ]) + return result.valid + } + } + + public class KdfClient { + weak var crypto: SynorCrypto? + + public func deriveKey(seed: Data, config: DerivationConfig = DerivationConfig()) async throws -> Data { + var body: [String: Any] = [ + "seed": seed.base64EncodedString(), + "output_length": config.outputLength + ] + if let salt = config.salt { + body["salt"] = salt.base64EncodedString() + } + if let info = config.info { + body["info"] = info.base64EncodedString() + } + + struct KeyResponse: Decodable { let key: String } + let result: KeyResponse = try await crypto!.post(path: "/kdf/hkdf", body: body) + return Data(base64Encoded: result.key) ?? Data() + } + + public func deriveFromPassword(password: Data, config: PasswordDerivationConfig) async throws -> Data { + struct KeyResponse: Decodable { let key: String } + let result: KeyResponse = try await crypto!.post(path: "/kdf/pbkdf2", body: [ + "password": password.base64EncodedString(), + "salt": config.salt.base64EncodedString(), + "iterations": config.iterations, + "output_length": config.outputLength + ]) + return Data(base64Encoded: result.key) ?? Data() + } + } + + public class HashClient { + weak var crypto: SynorCrypto? + + public func sha3_256(data: Data) async throws -> Hash256 { + try await crypto!.post(path: "/hash/sha3-256", body: ["data": data.base64EncodedString()]) + } + + public func blake3(data: Data) async throws -> Hash256 { + try await crypto!.post(path: "/hash/blake3", body: ["data": data.base64EncodedString()]) + } + + public func keccak256(data: Data) async throws -> Hash256 { + try await crypto!.post(path: "/hash/keccak256", body: ["data": data.base64EncodedString()]) + } + } +} diff --git a/sdk/swift/Sources/SynorCrypto/Types.swift b/sdk/swift/Sources/SynorCrypto/Types.swift new file mode 100644 index 0000000..559e067 --- /dev/null +++ b/sdk/swift/Sources/SynorCrypto/Types.swift @@ -0,0 +1,499 @@ +import Foundation + +// MARK: - Enumerations + +/// Network type for the Synor blockchain. +public enum Network: String, Codable, CaseIterable { + case mainnet + case testnet + case devnet +} + +/// Falcon signature variant. +public enum FalconVariant: String, Codable, CaseIterable { + case falcon512 + case falcon1024 + + public var signatureSize: Int { + switch self { + case .falcon512: return 690 + case .falcon1024: return 1330 + } + } + + public var publicKeySize: Int { + switch self { + case .falcon512: return 897 + case .falcon1024: return 1793 + } + } + + public var securityLevel: Int { + switch self { + case .falcon512: return 128 + case .falcon1024: return 256 + } + } +} + +/// SPHINCS+ signature variant. +public enum SphincsVariant: String, Codable, CaseIterable { + case shake128s + case shake192s + case shake256s + + public var signatureSize: Int { + switch self { + case .shake128s: return 7856 + case .shake192s: return 16224 + case .shake256s: return 29792 + } + } + + public var securityLevel: Int { + switch self { + case .shake128s: return 128 + case .shake192s: return 192 + case .shake256s: return 256 + } + } +} + +/// Post-quantum algorithm types. +public enum PqAlgorithm: String, Codable { + case dilithium3 + case falcon512 + case falcon1024 + case sphincs128s + case sphincs192s + case sphincs256s +} + +/// Cryptographic algorithm family. +public enum AlgorithmFamily: String, Codable { + case classical + case lattice + case hashBased = "hash_based" + case hybrid +} + +// MARK: - Configuration Types + +/// Configuration for the Crypto SDK. +public struct CryptoConfig { + public let apiKey: String + public var endpoint: String + public var timeout: TimeInterval + public var retries: Int + public var debug: Bool + public var defaultNetwork: Network + + public init( + apiKey: String, + endpoint: String = "https://crypto.synor.io/v1", + timeout: TimeInterval = 30.0, + retries: Int = 3, + debug: Bool = false, + defaultNetwork: Network = .mainnet + ) { + self.apiKey = apiKey + self.endpoint = endpoint + self.timeout = timeout + self.retries = retries + self.debug = debug + self.defaultNetwork = defaultNetwork + } +} + +/// Configuration for key derivation. +public struct DerivationConfig { + public var salt: Data? + public var info: Data? + public var outputLength: Int + + public init(salt: Data? = nil, info: Data? = nil, outputLength: Int = 32) { + self.salt = salt + self.info = info + self.outputLength = outputLength + } +} + +/// Configuration for password-based key derivation. +public struct PasswordDerivationConfig { + public let salt: Data + public var iterations: Int + public var outputLength: Int + + public init(salt: Data, iterations: Int = 100000, outputLength: Int = 32) { + self.salt = salt + self.iterations = iterations + self.outputLength = outputLength + } +} + +/// BIP-44 derivation path. +public struct DerivationPath: CustomStringConvertible { + public static let coinType = 0x5359 + + public let account: Int + public let change: Int + public let index: Int + + public init(account: Int, change: Int, index: Int) { + self.account = account + self.change = change + self.index = index + } + + public static func external(account: Int, index: Int) -> DerivationPath { + DerivationPath(account: account, change: 0, index: index) + } + + public static func `internal`(account: Int, index: Int) -> DerivationPath { + DerivationPath(account: account, change: 1, index: index) + } + + public var description: String { + "m/44'/\(Self.coinType)'/\(account)'/\(change)/\(index)" + } + + public func toDict() -> [String: Int] { + ["account": account, "change": change, "index": index] + } +} + +// MARK: - Key Types + +/// Hybrid public key combining Ed25519 and Dilithium3. +public struct HybridPublicKey: Codable { + public let ed25519: String + public let dilithium: String + + public var ed25519Bytes: Data { + Data(base64Encoded: ed25519) ?? Data() + } + + public var dilithiumBytes: Data { + Data(base64Encoded: dilithium) ?? Data() + } + + public var size: Int { + ed25519Bytes.count + dilithiumBytes.count + } + + public func toDict() -> [String: String] { + ["ed25519": ed25519, "dilithium": dilithium] + } +} + +/// Secret key for hybrid signatures. +public struct SecretKey: Codable { + public let ed25519Seed: String + public let masterSeed: String + + enum CodingKeys: String, CodingKey { + case ed25519Seed = "ed25519_seed" + case masterSeed = "master_seed" + } + + public var ed25519SeedBytes: Data { + Data(base64Encoded: ed25519Seed) ?? Data() + } + + public var masterSeedBytes: Data { + Data(base64Encoded: masterSeed) ?? Data() + } + + public func toDict() -> [String: String] { + ["ed25519_seed": ed25519Seed, "master_seed": masterSeed] + } +} + +/// Falcon public key. +public struct FalconPublicKey: Codable { + public let variant: String + public let bytes: String + + public var variantEnum: FalconVariant { + FalconVariant(rawValue: variant) ?? .falcon512 + } + + public var keyBytes: Data { + Data(base64Encoded: bytes) ?? Data() + } +} + +/// Falcon secret key. +public struct FalconSecretKey: Codable { + public let variant: String + public let bytes: String + + public var variantEnum: FalconVariant { + FalconVariant(rawValue: variant) ?? .falcon512 + } + + public var keyBytes: Data { + Data(base64Encoded: bytes) ?? Data() + } +} + +/// SPHINCS+ public key. +public struct SphincsPublicKey: Codable { + public let variant: String + public let bytes: String + + public var variantEnum: SphincsVariant { + SphincsVariant(rawValue: variant) ?? .shake128s + } + + public var keyBytes: Data { + Data(base64Encoded: bytes) ?? Data() + } +} + +/// SPHINCS+ secret key. +public struct SphincsSecretKey: Codable { + public let variant: String + public let bytes: String + + public var variantEnum: SphincsVariant { + SphincsVariant(rawValue: variant) ?? .shake128s + } + + public var keyBytes: Data { + Data(base64Encoded: bytes) ?? Data() + } +} + +// MARK: - Signature Types + +/// Hybrid signature combining Ed25519 and Dilithium3. +public struct HybridSignature: Codable { + public let ed25519: String + public let dilithium: String + + public var ed25519Bytes: Data { + Data(base64Encoded: ed25519) ?? Data() + } + + public var dilithiumBytes: Data { + Data(base64Encoded: dilithium) ?? Data() + } + + public var size: Int { + ed25519Bytes.count + dilithiumBytes.count + } + + public var bytes: Data { + var result = Data() + result.append(ed25519Bytes) + result.append(dilithiumBytes) + return result + } + + public func toDict() -> [String: String] { + ["ed25519": ed25519, "dilithium": dilithium] + } +} + +/// Falcon signature. +public struct FalconSignature: Codable { + public let variant: String + public let signature: String + + public var variantEnum: FalconVariant { + FalconVariant(rawValue: variant) ?? .falcon512 + } + + public var signatureBytes: Data { + Data(base64Encoded: signature) ?? Data() + } + + public var size: Int { + signatureBytes.count + } +} + +/// SPHINCS+ signature. +public struct SphincsSignature: Codable { + public let variant: String + public let signature: String + + public var variantEnum: SphincsVariant { + SphincsVariant(rawValue: variant) ?? .shake128s + } + + public var signatureBytes: Data { + Data(base64Encoded: signature) ?? Data() + } + + public var size: Int { + signatureBytes.count + } +} + +// MARK: - Keypair Types + +/// Hybrid keypair for Ed25519 + Dilithium3. +public struct HybridKeypair: Codable { + public let publicKey: HybridPublicKey + public let secretKey: SecretKey + public let addresses: [String: String] + + enum CodingKeys: String, CodingKey { + case publicKey = "public_key" + case secretKey = "secret_key" + case addresses + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + publicKey = try container.decode(HybridPublicKey.self, forKey: .publicKey) + secretKey = try container.decode(SecretKey.self, forKey: .secretKey) + addresses = try container.decodeIfPresent([String: String].self, forKey: .addresses) ?? [:] + } + + public func address(for network: Network) -> String { + addresses[network.rawValue] ?? "" + } +} + +/// Falcon keypair. +public struct FalconKeypair: Codable { + public let variant: String + public let publicKey: FalconPublicKey + public let secretKey: FalconSecretKey + + enum CodingKeys: String, CodingKey { + case variant + case publicKey = "public_key" + case secretKey = "secret_key" + } + + public var variantEnum: FalconVariant { + FalconVariant(rawValue: variant) ?? .falcon512 + } +} + +/// SPHINCS+ keypair. +public struct SphincsKeypair: Codable { + public let variant: String + public let publicKey: SphincsPublicKey + public let secretKey: SphincsSecretKey + + enum CodingKeys: String, CodingKey { + case variant + case publicKey = "public_key" + case secretKey = "secret_key" + } + + public var variantEnum: SphincsVariant { + SphincsVariant(rawValue: variant) ?? .shake128s + } +} + +// MARK: - Mnemonic Types + +/// BIP-39 mnemonic phrase. +public struct Mnemonic: Codable { + public let phrase: String + public let words: [String]? + public let wordCount: Int? + public let entropy: String? + + enum CodingKeys: String, CodingKey { + case phrase + case words + case wordCount = "word_count" + case entropy + } + + public var wordList: [String] { + words ?? phrase.split(separator: " ").map(String.init) + } + + public var count: Int { + wordCount ?? wordList.count + } + + public var entropyBytes: Data { + entropy.flatMap { Data(base64Encoded: $0) } ?? Data() + } +} + +/// Mnemonic validation result. +public struct MnemonicValidation: Codable { + public let valid: Bool + public let error: String? +} + +// MARK: - Address Types + +/// Blockchain address. +public struct Address: Codable { + public let address: String + public let network: String + public let pubkeyHash: String? + + enum CodingKeys: String, CodingKey { + case address + case network + case pubkeyHash = "pubkey_hash" + } + + public var networkEnum: Network { + Network(rawValue: network) ?? .mainnet + } + + public var pubkeyHashBytes: Data { + pubkeyHash.flatMap { Data(base64Encoded: $0) } ?? Data() + } +} + +// MARK: - Hash Types + +/// 256-bit hash result. +public struct Hash256: Codable { + public let hash: String + + public var hex: String { hash } + + public var bytes: Data { + var result = Data() + var index = hash.startIndex + while index < hash.endIndex { + let nextIndex = hash.index(index, offsetBy: 2) + if let byte = UInt8(hash[index..