diff --git a/cmake/sources.cmake b/cmake/sources.cmake index e55bb6889..52d4dc636 100644 --- a/cmake/sources.cmake +++ b/cmake/sources.cmake @@ -66,6 +66,7 @@ set(INTERPRETER_SOURCES interpreter/src/builtins/builtin_module_basics.cpp interpreter/src/builtins/builtin_module_math.cpp interpreter/src/builtins/builtin_module_assertions.cpp + interpreter/src/builtins/builtin_module_crypto.cpp interpreter/src/builtins/builtin_module_async.cpp interpreter/src/builtins/builtin_module_maps.cpp interpreter/src/builtins/builtin_module_maps_core.cpp diff --git a/compiler/include/backend/llvm/builders/builtins/security_builtins_codegen.h b/compiler/include/backend/llvm/builders/builtins/security_builtins_codegen.h index 43b9f505c..1e6def6e4 100644 --- a/compiler/include/backend/llvm/builders/builtins/security_builtins_codegen.h +++ b/compiler/include/backend/llvm/builders/builtins/security_builtins_codegen.h @@ -38,6 +38,8 @@ class SecurityBuiltinsCodeGen llvm::Value *emitBuiltinSecurityTimestamp(std::shared_ptr inst); llvm::Value *emitBuiltinSecuritySecureRandom(std::shared_ptr inst); llvm::Value *emitBuiltinSecurityBase64Encode(std::shared_ptr inst); + llvm::Value *emitBuiltinCryptoBlake3Hash(std::shared_ptr inst); + llvm::Value *emitBuiltinCryptoBlake3KeyedHash(std::shared_ptr inst); }; }} // namespace Sad::LLVM diff --git a/compiler/include/backend/llvm/llvm_codegen.h b/compiler/include/backend/llvm/llvm_codegen.h index cc6fa1478..ea2ba0a03 100644 --- a/compiler/include/backend/llvm/llvm_codegen.h +++ b/compiler/include/backend/llvm/llvm_codegen.h @@ -944,6 +944,8 @@ namespace Sad llvm::Value *emitBuiltinSecurityTimestamp(std::shared_ptr inst) { return secb_->emitBuiltinSecurityTimestamp(inst); } // وقت_الآن llvm::Value *emitBuiltinSecuritySecureRandom(std::shared_ptr inst) { return secb_->emitBuiltinSecuritySecureRandom(inst); } // عشوائي_آمن llvm::Value *emitBuiltinSecurityBase64Encode(std::shared_ptr inst) { return secb_->emitBuiltinSecurityBase64Encode(inst); } // ترميز_64 + llvm::Value *emitBuiltinCryptoBlake3Hash(std::shared_ptr inst) { return secb_->emitBuiltinCryptoBlake3Hash(inst); } // بلايك3 + llvm::Value *emitBuiltinCryptoBlake3KeyedHash(std::shared_ptr inst) { return secb_->emitBuiltinCryptoBlake3KeyedHash(inst); } // هاش_مفتاح // ================================================================ // التكامل مع C/C++ — FFI Functions (20) diff --git a/compiler/include/frontend/builders/builtin_builder.h b/compiler/include/frontend/builders/builtin_builder.h index d0cb5c80b..b2eaa3b5b 100644 --- a/compiler/include/frontend/builders/builtin_builder.h +++ b/compiler/include/frontend/builders/builtin_builder.h @@ -71,6 +71,7 @@ namespace Sad std::optional buildBuiltinSystem_OsSystem(const std::string &funcName, bool isUserDefinedFunction, std::vector &argResults, std::vector &argOperands); std::optional buildBuiltinSystem_Security(const std::string &funcName, bool isUserDefinedFunction, std::vector &argResults, std::vector &argOperands); + std::optional buildBuiltinSystem_Crypto(const std::string &funcName, bool isUserDefinedFunction, std::vector &argResults, std::vector &argOperands); std::optional buildBuiltinCallSimd(const std::string &funcName, bool isUserDefinedFunction, std::vector &argResults, std::vector &argOperands); diff --git a/compiler/include/frontend/sir_builder.h b/compiler/include/frontend/sir_builder.h index 87613b318..7820125d9 100644 --- a/compiler/include/frontend/sir_builder.h +++ b/compiler/include/frontend/sir_builder.h @@ -1963,6 +1963,10 @@ namespace Sad { return builtins_->buildBuiltinSystem_Security(funcName, isUserDefinedFunction, argResults, argOperands); } + std::optional buildBuiltinSystem_Crypto(const std::string &funcName, bool isUserDefinedFunction, std::vector &argResults, std::vector &argOperands) + { + return builtins_->buildBuiltinSystem_Crypto(funcName, isUserDefinedFunction, argResults, argOperands); + } std::optional buildBuiltinSystem_FFI(const std::string &funcName, bool isUserDefinedFunction, std::vector &argResults, std::vector &argOperands) { diff --git a/compiler/include/frontend/sir_types.h b/compiler/include/frontend/sir_types.h index 7c2734cb7..c11014289 100644 --- a/compiler/include/frontend/sir_types.h +++ b/compiler/include/frontend/sir_types.h @@ -464,6 +464,12 @@ namespace Sad BUILTIN_SECURITY_SECURE_RANDOM, ///< عشوائي_آمن / Secure random number BUILTIN_SECURITY_BASE64_ENCODE, ///< ترميز_64 / Base64 encode + // ========================================== + // 12ب. وحدة تشفير — Crypto module builtins (BLAKE3) + // ========================================== + BUILTIN_CRYPTO_BLAKE3_HASH, ///< بلايك3 / BLAKE3 hash - hex string + BUILTIN_CRYPTO_BLAKE3_KEYED_HASH, ///< هاش_مفتاح / BLAKE3 keyed hash (MAC) - hex string + // ========================================== // 13. التكامل مع C/C++ — FFI Functions (15) // ========================================== diff --git a/compiler/src/backend/llvm/builders/builtins/security_builtins_ops.cpp b/compiler/src/backend/llvm/builders/builtins/security_builtins_ops.cpp index 79714fc0a..8943b0f70 100644 --- a/compiler/src/backend/llvm/builders/builtins/security_builtins_ops.cpp +++ b/compiler/src/backend/llvm/builders/builtins/security_builtins_ops.cpp @@ -472,4 +472,42 @@ namespace Sad { namespace LLVM { + llvm::Value *SecurityBuiltinsCodeGen::emitBuiltinCryptoBlake3Hash(std::shared_ptr inst) + { + if (!inst || inst->operands.empty()) + return nullptr; + llvm::Value *val = cg_.resolveOperand(inst->operands[0]); + if (!val) + return nullptr; + // Call runtime sad_blake3_hash(const char*) -> char* (BLAKE3 hex string, + // matching the interpreter — interpreter/src/builtins/builtin_module_crypto.cpp) + llvm::Type *i8Ptr = llvm::Type::getInt8Ty(*cg_.context_)->getPointerTo(); + llvm::FunctionType *ft = llvm::FunctionType::get(i8Ptr, {i8Ptr}, false); + llvm::FunctionCallee fn = cg_.module_->getOrInsertFunction("sad_blake3_hash", ft); + llvm::Value *result = cg_.builder_->CreateCall(fn, {val}, "blake3.ret"); + if (inst->result.has_value()) + cg_.context_info_.namedValues[inst->result->name] = result; + return result; + } + + + + llvm::Value *SecurityBuiltinsCodeGen::emitBuiltinCryptoBlake3KeyedHash(std::shared_ptr inst) + { + if (!inst || inst->operands.size() < 2) + return nullptr; + llvm::Value *data = cg_.resolveOperand(inst->operands[0]); + llvm::Value *key = cg_.resolveOperand(inst->operands[1]); + if (!data || !key) + return nullptr; + // Call runtime sad_blake3_keyed_hash(const char*, const char*) -> char* + llvm::Type *i8Ptr = llvm::Type::getInt8Ty(*cg_.context_)->getPointerTo(); + llvm::FunctionType *ft = llvm::FunctionType::get(i8Ptr, {i8Ptr, i8Ptr}, false); + llvm::FunctionCallee fn = cg_.module_->getOrInsertFunction("sad_blake3_keyed_hash", ft); + llvm::Value *result = cg_.builder_->CreateCall(fn, {data, key}, "blake3_keyed.ret"); + if (inst->result.has_value()) + cg_.context_info_.namedValues[inst->result->name] = result; + return result; + } + }} // namespace Sad::LLVM diff --git a/compiler/src/backend/llvm/builders/core/instr_core_ops.cpp b/compiler/src/backend/llvm/builders/core/instr_core_ops.cpp index ddcbc3b77..90bf2973f 100644 --- a/compiler/src/backend/llvm/builders/core/instr_core_ops.cpp +++ b/compiler/src/backend/llvm/builders/core/instr_core_ops.cpp @@ -228,6 +228,10 @@ namespace Sad return cg_.emitBuiltinSecuritySecureRandom(inst); case SIROpcode::BUILTIN_SECURITY_BASE64_ENCODE: return cg_.emitBuiltinSecurityBase64Encode(inst); + case SIROpcode::BUILTIN_CRYPTO_BLAKE3_HASH: + return cg_.emitBuiltinCryptoBlake3Hash(inst); + case SIROpcode::BUILTIN_CRYPTO_BLAKE3_KEYED_HASH: + return cg_.emitBuiltinCryptoBlake3KeyedHash(inst); // ===== FFI (20) ===== case SIROpcode::FFI_PRINTF: diff --git a/compiler/src/backend/llvm/llvm_codegen_instructions.cpp b/compiler/src/backend/llvm/llvm_codegen_instructions.cpp index a3bada0c9..4b57c6724 100644 --- a/compiler/src/backend/llvm/llvm_codegen_instructions.cpp +++ b/compiler/src/backend/llvm/llvm_codegen_instructions.cpp @@ -88,6 +88,7 @@ namespace Sad namespace Nio = Sad::Builtins::Names::CompilerIo; namespace Nm = Sad::Builtins::Names::Math; namespace Na = Sad::Builtins::Names::Assertions; + namespace Ncr = Sad::Builtins::Names::Crypto; namespace Nar = Sad::Builtins::Names::Arrays; namespace Nmap = Sad::Builtins::Names::Maps; namespace Nasync = Sad::Builtins::Names::AsyncAdvanced; @@ -205,6 +206,12 @@ namespace Sad case SIROpcode::BUILTIN_SECURITY_DECRYPT: return std::string(Na::DECRYPT); // فك_تشفير (runtime مستضاف) case SIROpcode::BUILTIN_SECURITY_SANITIZE: return std::string(Na::SANITIZE); // نظف (runtime مستضاف) case SIROpcode::BUILTIN_SECURITY_BASE64_ENCODE: return std::string(Na::BASE64_ENCODE); // ترميز_64 (runtime مستضاف) + // (AR) نفس بوّابة عائلة الأمن المستضافة أعلاه — بلايك3/هاش_مفتاح + // يستدعيان sad_blake3_* من نفس runtime المستضاف المضمَّن. + // (EN) Same hosted-runtime gate as the security family above — + // BLAKE3 functions call into the same embedded runtime. + case SIROpcode::BUILTIN_CRYPTO_BLAKE3_HASH: return std::string(Ncr::BLAKE3_HASH); // بلايك3 (runtime مستضاف) + case SIROpcode::BUILTIN_CRYPTO_BLAKE3_KEYED_HASH: return std::string(Ncr::BLAKE3_KEYED_HASH); // هاش_مفتاح (runtime مستضاف) default: return std::string(); } } diff --git a/compiler/src/frontend/builders/builtins_crypto.cpp b/compiler/src/frontend/builders/builtins_crypto.cpp new file mode 100644 index 000000000..921b64568 --- /dev/null +++ b/compiler/src/frontend/builders/builtins_crypto.cpp @@ -0,0 +1,85 @@ +// ============================================================================ +// builtins_crypto.cpp +// (AR) دوال وحدة تشفير: بلايك3 (BLAKE3 hash)، هاش_مفتاح (BLAKE3 keyed hash) +// (EN) Crypto module builtins: BLAKE3 hash, BLAKE3 keyed hash (MAC) +// ============================================================================ + +#include "sir_builder.h" +#include "builders/builtin_builder.h" +#include "module_nodes.h" +#include "module_resolver.h" +#include "lexer_core.h" +#include "parser_core.h" +#include "pattern_nodes.h" +#include "utf8_utils.h" +#include +#include +#include + +#include "builtin_registry.h" +namespace Bn = Sad::Builtins::Names; + +namespace Sad +{ + namespace Compiler + { + namespace SIR + { + + std::optional BuiltinBuilder::buildBuiltinSystem_Crypto( + const std::string &funcName, + bool isUserDefinedFunction, + std::vector &argResults, + std::vector &argOperands) + { + (void)isUserDefinedFunction; + + // 1. بلايك3 / blake3 - هاش BLAKE3 (256 بت، ذاتيّ التنفيذ، مطابق للمفسّر) + if (funcName == Bn::Crypto::BLAKE3_HASH) + { + if (argResults.empty()) + { + std::cerr << "[خطأ] دالة بلايك3 تتطلب معامل واحد (النص)" << std::endl; + return BuildResult("", SadTypeKind::String); + } + std::string resultReg = b_.newTempRegister(); + SIROperand resultOp = SIROperand::Register(resultReg, SadTypeKind::String); + SIRInstruction inst(SIROpcode::BUILTIN_CRYPTO_BLAKE3_HASH); + inst.result = resultOp; + inst.operands.push_back(argOperands[0]); + if (b_.currentBlock_) + b_.currentBlock_->instructions.push_back(inst); +#ifndef NDEBUG + std::cout << "[DEBUG] builtin " << funcName << "() -> " << resultReg << std::endl; +#endif + return BuildResult(resultReg, SadTypeKind::String); + } + + // 2. هاش_مفتاح / keyed_hash - مصادقة رسالة عبر نمط BLAKE3 المُفتاح + if (funcName == Bn::Crypto::BLAKE3_KEYED_HASH) + { + if (argResults.size() < 2) + { + std::cerr << "[خطأ] دالة هاش_مفتاح تتطلب معاملين (النص، المفتاح)" << std::endl; + return BuildResult("", SadTypeKind::String); + } + std::string resultReg = b_.newTempRegister(); + SIROperand resultOp = SIROperand::Register(resultReg, SadTypeKind::String); + SIRInstruction inst(SIROpcode::BUILTIN_CRYPTO_BLAKE3_KEYED_HASH); + inst.result = resultOp; + inst.operands.push_back(argOperands[0]); + inst.operands.push_back(argOperands[1]); + if (b_.currentBlock_) + b_.currentBlock_->instructions.push_back(inst); +#ifndef NDEBUG + std::cout << "[DEBUG] builtin " << funcName << "() -> " << resultReg << std::endl; +#endif + return BuildResult(resultReg, SadTypeKind::String); + } + + return std::nullopt; + } + + } // namespace SIR + } // namespace Compiler +} // namespace Sad diff --git a/compiler/src/frontend/builders/builtins_system.cpp b/compiler/src/frontend/builders/builtins_system.cpp index 78178fc43..5fcbed872 100644 --- a/compiler/src/frontend/builders/builtins_system.cpp +++ b/compiler/src/frontend/builders/builtins_system.cpp @@ -471,6 +471,10 @@ namespace Sad auto securityResult = buildBuiltinSystem_Security(funcName, isUserDefinedFunction, argResults, argOperands); if (securityResult) return securityResult; + // Crypto module functions (BLAKE3 hash, keyed hash) + auto cryptoResult = buildBuiltinSystem_Crypto(funcName, isUserDefinedFunction, argResults, argOperands); + if (cryptoResult) return cryptoResult; + // FFI functions (printf, malloc, fopen, etc.) auto ffiResult = buildBuiltinSystem_FFI(funcName, isUserDefinedFunction, argResults, argOperands); if (ffiResult) return ffiResult; diff --git a/interpreter/src/builtins/builtin_module_crypto.cpp b/interpreter/src/builtins/builtin_module_crypto.cpp new file mode 100644 index 000000000..5c78824da --- /dev/null +++ b/interpreter/src/builtins/builtin_module_crypto.cpp @@ -0,0 +1,378 @@ +/** + * @file builtin_module_crypto.cpp + * @brief (AR) وحدة تشفير — دوال هاش/MAC حديثة (BLAKE3)، ذاتيّة التنفيذ بالكامل + * @brief (EN) Crypto module — modern hash/MAC functions (BLAKE3), fully self-implemented + * + * @details + * (AR) تنفيذ BLAKE3 مرجعيّ محمول (بلا SIMD/تعدّد خيوط) — مطابق حرفيًّا لنظير + * المترجم في tools/compiler/runtime/sad_embedded_runtime.c (دوال + * sad_blake3_*). كلاهما تحقّق بنجاح مقابل شعاعات BLAKE3 الرسميّة + * (test_vectors.json من مستودع BLAKE3-team) قبل الدمج. + * + * @see builtin_registry.cpp — التسجيل المركزي + */ + +#include "builtin_common.h" +#include "builtin_registry.h" +namespace Bcr = Sad::Builtins::Names::Crypto; +#include +#include +#include + +namespace Sad +{ + namespace Interpreter + { + namespace CryptoDetail + { + // ════════════════════════════════════════════════════════════ + // (AR) تنفيذ BLAKE3 مرجعيّ — مطابق للنسخة C في وقت تشغيل المترجم + // (EN) Reference BLAKE3 — must stay byte-identical to the C + // version in the compiler's embedded runtime. + // ════════════════════════════════════════════════════════════ + constexpr uint32_t IV[8] = { + 0x6A09E667u, 0xBB67AE85u, 0x3C6EF372u, 0xA54FF53Au, + 0x510E527Fu, 0x9B05688Cu, 0x1F83D9ABu, 0x5BE0CD19u}; + constexpr int MSG_PERM[16] = {2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8}; + + constexpr uint32_t CHUNK_START = 1u; + constexpr uint32_t CHUNK_END = 2u; + constexpr uint32_t PARENT = 4u; + constexpr uint32_t ROOT = 8u; + constexpr uint32_t KEYED_HASH = 16u; + constexpr int BLOCK_LEN = 64; + constexpr int CHUNK_LEN = 1024; + + inline uint32_t rotr32(uint32_t x, int n) { return (x >> n) | (x << (32 - n)); } + + inline void g(uint32_t *st, int a, int b, int c, int d, uint32_t mx, uint32_t my) + { + st[a] = st[a] + st[b] + mx; + st[d] = rotr32(st[d] ^ st[a], 16); + st[c] = st[c] + st[d]; + st[b] = rotr32(st[b] ^ st[c], 12); + st[a] = st[a] + st[b] + my; + st[d] = rotr32(st[d] ^ st[a], 8); + st[c] = st[c] + st[d]; + st[b] = rotr32(st[b] ^ st[c], 7); + } + + inline void round_fn(uint32_t *st, const uint32_t *m) + { + g(st, 0, 4, 8, 12, m[0], m[1]); + g(st, 1, 5, 9, 13, m[2], m[3]); + g(st, 2, 6, 10, 14, m[4], m[5]); + g(st, 3, 7, 11, 15, m[6], m[7]); + g(st, 0, 5, 10, 15, m[8], m[9]); + g(st, 1, 6, 11, 12, m[10], m[11]); + g(st, 2, 7, 8, 13, m[12], m[13]); + g(st, 3, 4, 9, 14, m[14], m[15]); + } + + inline void permute(uint32_t *m) + { + uint32_t t[16]; + for (int i = 0; i < 16; ++i) + t[i] = m[MSG_PERM[i]]; + std::memcpy(m, t, sizeof(t)); + } + + inline void compress(const uint32_t cv[8], const uint32_t block_words[16], + uint64_t counter, uint32_t block_len, uint32_t flags, + uint32_t out[16]) + { + uint32_t st[16]; + uint32_t m[16]; + std::memcpy(st, cv, 8 * sizeof(uint32_t)); + std::memcpy(st + 8, IV, 4 * sizeof(uint32_t)); + st[12] = (uint32_t)(counter & 0xFFFFFFFFu); + st[13] = (uint32_t)(counter >> 32); + st[14] = block_len; + st[15] = flags; + std::memcpy(m, block_words, 16 * sizeof(uint32_t)); + for (int r = 0; r < 7; ++r) + { + round_fn(st, m); + if (r < 6) + permute(m); + } + for (int i = 0; i < 8; ++i) + { + out[i] = st[i] ^ st[i + 8]; + out[i + 8] = st[i + 8] ^ cv[i]; + } + } + + inline void words_from_bytes_padded(const uint8_t *b, size_t len, uint32_t out[16]) + { + uint8_t buf[64]; + std::memset(buf, 0, 64); + if (len) + std::memcpy(buf, b, len); + for (int i = 0; i < 16; ++i) + out[i] = (uint32_t)buf[i * 4] | ((uint32_t)buf[i * 4 + 1] << 8) | + ((uint32_t)buf[i * 4 + 2] << 16) | ((uint32_t)buf[i * 4 + 3] << 24); + } + + struct ChunkState + { + uint32_t cv[8]; + uint64_t chunk_counter; + uint8_t block[BLOCK_LEN]; + size_t block_len = 0; + int blocks_compressed = 0; + uint32_t flags; + + void init(const uint32_t key[8], uint64_t counter, uint32_t f) + { + std::memcpy(cv, key, 8 * sizeof(uint32_t)); + chunk_counter = counter; + block_len = 0; + blocks_compressed = 0; + flags = f; + } + size_t len() const { return (size_t)BLOCK_LEN * blocks_compressed + block_len; } + uint32_t start_flag() const { return blocks_compressed == 0 ? CHUNK_START : 0; } + + void update(const uint8_t *data, size_t n) + { + while (n > 0) + { + if (block_len == BLOCK_LEN) + { + uint32_t block_words[16]; + uint32_t out[16]; + words_from_bytes_padded(block, BLOCK_LEN, block_words); + compress(cv, block_words, chunk_counter, BLOCK_LEN, + flags | start_flag(), out); + std::memcpy(cv, out, 8 * sizeof(uint32_t)); + blocks_compressed++; + block_len = 0; + } + size_t take = (size_t)BLOCK_LEN - block_len; + if (take > n) + take = n; + std::memcpy(block + block_len, data, take); + block_len += take; + data += take; + n -= take; + } + } + }; + + struct Output + { + uint32_t input_cv[8]; + uint32_t block_words[16]; + uint64_t counter; + uint32_t block_len; + uint32_t flags; + + void chaining_value(uint32_t cv[8]) const + { + uint32_t out[16]; + compress(input_cv, block_words, counter, block_len, flags, out); + std::memcpy(cv, out, 8 * sizeof(uint32_t)); + } + void root_bytes(uint8_t *out, size_t out_len) const + { + uint64_t block_counter = 0; + size_t written = 0; + while (written < out_len) + { + uint32_t words[16]; + compress(input_cv, block_words, block_counter, block_len, + flags | ROOT, words); + for (int i = 0; i < 16 && written < out_len; ++i) + { + uint8_t b[4] = {(uint8_t)words[i], (uint8_t)(words[i] >> 8), + (uint8_t)(words[i] >> 16), (uint8_t)(words[i] >> 24)}; + size_t n = 4; + if (n > out_len - written) + n = out_len - written; + std::memcpy(out + written, b, n); + written += n; + } + block_counter++; + } + } + }; + + inline void chunk_state_output(const ChunkState &cs, Output &out) + { + std::memcpy(out.input_cv, cs.cv, 8 * sizeof(uint32_t)); + words_from_bytes_padded(cs.block, cs.block_len, out.block_words); + out.counter = cs.chunk_counter; + out.block_len = (uint32_t)cs.block_len; + out.flags = cs.flags | cs.start_flag() | CHUNK_END; + } + + inline void parent_output(const uint32_t left_cv[8], const uint32_t right_cv[8], + const uint32_t key[8], uint32_t flags, Output &out) + { + std::memcpy(out.input_cv, key, 8 * sizeof(uint32_t)); + std::memcpy(out.block_words, left_cv, 8 * sizeof(uint32_t)); + std::memcpy(out.block_words + 8, right_cv, 8 * sizeof(uint32_t)); + out.counter = 0; + out.block_len = BLOCK_LEN; + out.flags = flags | PARENT; + } + + struct Hasher + { + uint32_t key[8]; + ChunkState chunk_state; + uint32_t cv_stack[54][8]; + int cv_stack_len = 0; + uint32_t flags; + + void init_internal(const uint32_t k[8], uint32_t f) + { + std::memcpy(key, k, 8 * sizeof(uint32_t)); + chunk_state.init(k, 0, f); + cv_stack_len = 0; + flags = f; + } + void init() { init_internal(IV, 0); } + void init_keyed(const uint8_t k[32]) + { + uint32_t key_words[8]; + for (int i = 0; i < 8; ++i) + key_words[i] = (uint32_t)k[i * 4] | ((uint32_t)k[i * 4 + 1] << 8) | + ((uint32_t)k[i * 4 + 2] << 16) | ((uint32_t)k[i * 4 + 3] << 24); + init_internal(key_words, KEYED_HASH); + } + + void add_chunk_cv(uint32_t new_cv[8], uint64_t total_chunks) + { + while ((total_chunks & 1) == 0) + { + uint32_t left[8]; + cv_stack_len--; + std::memcpy(left, cv_stack[cv_stack_len], 8 * sizeof(uint32_t)); + Output po; + parent_output(left, new_cv, key, flags, po); + po.chaining_value(new_cv); + total_chunks >>= 1; + } + std::memcpy(cv_stack[cv_stack_len], new_cv, 8 * sizeof(uint32_t)); + cv_stack_len++; + } + + void update(const uint8_t *data, size_t len) + { + while (len > 0) + { + if (chunk_state.len() == (size_t)CHUNK_LEN) + { + Output co; + uint32_t chunk_cv[8]; + chunk_state_output(chunk_state, co); + co.chaining_value(chunk_cv); + uint64_t total_chunks = chunk_state.chunk_counter + 1; + add_chunk_cv(chunk_cv, total_chunks); + chunk_state.init(key, total_chunks, flags); + } + size_t take = (size_t)CHUNK_LEN - chunk_state.len(); + if (take > len) + take = len; + chunk_state.update(data, take); + data += take; + len -= take; + } + } + + void finalize(uint8_t *out, size_t out_len) + { + Output output; + chunk_state_output(chunk_state, output); + int remaining = cv_stack_len; + while (remaining > 0) + { + uint32_t cv[8]; + remaining--; + output.chaining_value(cv); + Output po; + parent_output(cv_stack[remaining], cv, key, flags, po); + output = po; + } + output.root_bytes(out, out_len); + } + }; + + inline std::string to_hex(const uint8_t *b, size_t n) + { + static const char *hx = "0123456789abcdef"; + std::string s; + s.resize(n * 2); + for (size_t i = 0; i < n; ++i) + { + s[i * 2] = hx[b[i] >> 4]; + s[i * 2 + 1] = hx[b[i] & 0xF]; + } + return s; + } + + inline std::string blake3_hash_hex(const std::string &data) + { + Hasher h; + h.init(); + h.update(reinterpret_cast(data.data()), data.size()); + uint8_t digest[32]; + h.finalize(digest, 32); + return to_hex(digest, 32); + } + + inline void blake3_hash_raw(const std::string &data, uint8_t out[32]) + { + Hasher h; + h.init(); + h.update(reinterpret_cast(data.data()), data.size()); + h.finalize(out, 32); + } + + inline std::string blake3_keyed_hex(const std::string &data, const std::string &key) + { + uint8_t key32[32]; + if (key.size() == 32) + std::memcpy(key32, key.data(), 32); + else + blake3_hash_raw(key, key32); + Hasher h; + h.init_keyed(key32); + h.update(reinterpret_cast(data.data()), data.size()); + uint8_t digest[32]; + h.finalize(digest, 32); + return to_hex(digest, 32); + } + } // namespace CryptoDetail + + void registerBuiltinsCrypto(Interpreter &interpreter) + { + // بلايك3 / blake3 — هاش BLAKE3 (256 بت، سلسلة ست عشريّة 64 حرفًا) + auto blake3_func = [](Sad::Interpreter::BuiltinContext &ctx) -> std::shared_ptr + { + const auto &args = ctx.args(); + if (args.empty()) + ctx.error(::Sad::Errors::ErrorCode::RUN_BUILTIN_REQUIRES_ARG); + std::string input = args[0]->toString(); + return std::make_shared(CryptoDetail::blake3_hash_hex(input)); + }; + interpreter.getFunctionManager().registerBuiltinFunction(std::string(Bcr::BLAKE3_HASH), blake3_func); + + // هاش_مفتاح / keyed_hash — مصادقة رسالة عبر نمط BLAKE3 المُفتاح + auto keyed_hash_func = [](Sad::Interpreter::BuiltinContext &ctx) -> std::shared_ptr + { + const auto &args = ctx.args(); + if (args.size() < 2) + ctx.error(::Sad::Errors::ErrorCode::RUN_BUILTIN_REQUIRES_ARG); + std::string data = args[0]->toString(); + std::string key = args[1]->toString(); + if (key.empty()) + ctx.error(::Sad::Errors::ErrorCode::RUN_BUILTIN_REQUIRES_ARG); + return std::make_shared(CryptoDetail::blake3_keyed_hex(data, key)); + }; + interpreter.getFunctionManager().registerBuiltinFunction(std::string(Bcr::BLAKE3_KEYED_HASH), keyed_hash_func); + } + } // namespace Interpreter +} // namespace Sad diff --git a/interpreter/src/builtins/builtin_registry.cpp b/interpreter/src/builtins/builtin_registry.cpp index 481451b2a..ed5b08d12 100644 --- a/interpreter/src/builtins/builtin_registry.cpp +++ b/interpreter/src/builtins/builtin_registry.cpp @@ -49,6 +49,7 @@ namespace Sad static const std::string kModMath = std::string(Builtins::getCanonicalModuleName(ModuleId::MATH)); static const std::string kModBasics = std::string(Builtins::getCanonicalModuleName(ModuleId::BASICS)); static const std::string kModAssertions = std::string(Builtins::getCanonicalModuleName(ModuleId::ASSERTIONS)); + static const std::string kModCrypto = std::string(Builtins::getCanonicalModuleName(ModuleId::CRYPTO)); static const std::string kModMaps = std::string(Builtins::getCanonicalModuleName(ModuleId::MAPS)); static const std::string kModAsync = std::string(Builtins::getCanonicalModuleName(ModuleId::ASYNC)); static const std::string kModPlatform = std::string(Builtins::getCanonicalModuleName(ModuleId::PLATFORM)); @@ -63,6 +64,7 @@ namespace Sad extern void registerBuiltinsBasics(Interpreter &interpreter); extern void registerBuiltinsMath(Interpreter &interpreter); extern void registerBuiltinsAssertions(Interpreter &interpreter); + extern void registerBuiltinsCrypto(Interpreter &interpreter); extern void registerBuiltinsAsync(Interpreter &interpreter); extern void registerBuiltinsMaps(Interpreter &interpreter); extern void registerBuiltinsKernelCPU(Interpreter &interpreter); @@ -262,6 +264,8 @@ namespace Sad {"نظّف", kModAssertions}, {"عشوائي_آمن", kModAssertions}, {"ترميز_64", kModAssertions}, + {"بلايك3", kModCrypto}, + {"هاش_مفتاح", kModCrypto}, {"طباعة_تنسيق", kModAssertions}, {"حجز", kModAssertions}, {"حرر", kModAssertions}, @@ -749,13 +753,14 @@ namespace Sad }, {}}); - // ─── تشفير / Crypto (Part 7 crypto functions) — SHA-256, تشفير/فك_تشفير ─── + // ─── تشفير / Crypto (Part 7 crypto + Part 41 BLAKE3) — SHA-256, تشفير/فك_تشفير، بلايك3 ─── registry.registerModule({"\xd8\xaa\xd8\xb4\xd9\x81\xd9\x8a\xd8\xb1", // تشفير - "SHA-256\xd8\x8c \xd8\xaa\xd8\xb4\xd9\x81\xd9\x8a\xd8\xb1 CTR\xd8\x8c \xd9\x87\xd8\xa7\xd8\xb4", - // SHA-256، تشفير CTR، هاش + "SHA-256\xd8\x8c \xd8\xaa\xd8\xb4\xd9\x81\xd9\x8a\xd8\xb1 CTR\xd8\x8c \xd9\x87\xd8\xa7\xd8\xb4\xd8\x8c BLAKE3", + // SHA-256، تشفير CTR، هاش، BLAKE3 [](Interpreter &interp) { registerBuiltinsAssertions(interp); // SHA-256, encrypt/decrypt, hash + registerBuiltinsCrypto(interp); // BLAKE3, keyed hash }, {}}); diff --git a/language-truth/builtins/_index.yaml b/language-truth/builtins/_index.yaml index bc281b23a..c0a99100a 100644 --- a/language-truth/builtins/_index.yaml +++ b/language-truth/builtins/_index.yaml @@ -9,6 +9,7 @@ files: - arrays.yaml - basics.yaml - assertions.yaml +- crypto.yaml - maps.yaml - sockets.yaml - http_client.yaml diff --git a/language-truth/builtins/crypto.yaml b/language-truth/builtins/crypto.yaml new file mode 100644 index 000000000..4d0117934 --- /dev/null +++ b/language-truth/builtins/crypto.yaml @@ -0,0 +1,71 @@ +version: 1 +namespace_group: Crypto +description_ar: وحدة تشفير (دوال هاش/MAC حديثة — بلايك3) +description_en: Crypto module (modern hash/MAC functions — BLAKE3) +functions: +- cpp_id: BLAKE3_HASH + canonical: بلايك3 + namespace: Crypto + category: MODULE_FUNCTION + require_import: true + module: CRYPTO + compiler_strategy: RUNTIME_CALL + since: 0.1.0 + status: stable + description_ar: >- + يُرجع هاش BLAKE3 لنصّ كسلسلة ست عشريّة من 64 حرفًا (256 بت). BLAKE3 أحدث + وأسرع من SHA-256 (المستعمَل في هاش)، وذاتيّ التنفيذ بالكامل (بلا OpenSSL) + فيعمل على هدف الوضع الحرّ كذلك. التنفيذ مطابق حرفيًّا بين المفسّر + (interpreter/src/builtins/builtin_module_crypto.cpp) والمترجم + (tools/compiler/runtime/sad_embedded_runtime.c، دالّة sad_blake3_hash) — + نفس المدخل يُنتج نفس الهاش عبر كلا المحرّكين. مُتحقَّق مقابل شعاعات + BLAKE3 الرسميّة (test_vectors.json من مستودع BLAKE3-team) لمجموعة أطوال + مدخلات تغطّي المسارات الحرجة (كتلة واحدة/عدّة كتل/عدّة قطع في شجرة + Merkle الداخليّة). + description_en: >- + Returns the BLAKE3 hash of a string as a 64-character hex string (256 + bits). BLAKE3 is newer and faster than SHA-256 (used by هاش), and fully + self-implemented (no OpenSSL), so it also works on the freestanding + target. Identical behavior between interpreter and compiler. Verified + against the official BLAKE3 test vectors across input-length boundaries + (single block / multi-block / multi-chunk Merkle tree). + params: + - نص — القيمة المراد حساب هاشها (تُحوَّل إلى نصّ عبر toString إن لم تكن نصًّا أصلًا). + examples: + - code: | + استورد تشفير + اطبع_سطر(بلايك3("")) + runnable: true + lookup_table: name_only +- cpp_id: BLAKE3_KEYED_HASH + canonical: هاش_مفتاح + namespace: Crypto + category: MODULE_FUNCTION + require_import: true + module: CRYPTO + compiler_strategy: RUNTIME_CALL + since: 0.1.0 + status: stable + description_ar: >- + مصادقة رسالة (Message Authentication) عبر نمط BLAKE3 المُفتاح (keyed + mode) الرسميّ — أحدث وأبسط من بناء HMAC التقليديّ (لا يحتاج BLAKE3 حماية + HMAC من هجمات امتداد الطول length-extension، فالنمط المُفتاح جزء أصيل من + تصميم الخوارزميّة). المفتاح: إن كان طوله 32 بايت يُستعمَل مباشرة؛ غير ذلك + يُشتقّ منه مفتاح 32 بايت عبر بلايك3(مفتاح) أوّلًا. يُرجع سلسلة ست عشريّة + من 64 حرفًا. مطابق حرفيًّا بين المفسّر والمترجم. + description_en: >- + Message authentication via BLAKE3's native keyed mode — modern and + simpler than classic HMAC (BLAKE3 doesn't need HMAC's length-extension + protection; keying is intrinsic to the design). If the key is exactly 32 + bytes it's used directly; otherwise a 32-byte key is derived via + بلايك3(key) first. Returns a 64-character hex string. Identical behavior + between interpreter and compiler. + params: + - نص — الرسالة المراد مصادقتها. + - مفتاح — المفتاح السرّيّ (أيّ طول؛ يُشتقّ منه مفتاح 32 بايت عند الحاجة). + examples: + - code: | + استورد تشفير + اطبع_سطر(هاش_مفتاح("رسالة", "سرّ")) + runnable: true + lookup_table: name_only diff --git a/scripts/codegen/gen_builtins_registry.py b/scripts/codegen/gen_builtins_registry.py index fce3cf853..016305237 100644 --- a/scripts/codegen/gen_builtins_registry.py +++ b/scripts/codegen/gen_builtins_registry.py @@ -88,6 +88,7 @@ "Arrays": ("عمليات المصفوفات المدمجة", "Built-in array operations"), "Basics": ("وحدة أساسيات", "Basics module"), "Assertions": ("وحدة تأكيدات", "Assertions module"), + "Crypto": ("وحدة تشفير", "Crypto module"), "Maps": ("وحدة خرائط", "Maps module"), "Sockets": ("وحدة مقابس", "Sockets module"), "HttpClient": ("عميل HTTP", "HTTP Client"), diff --git a/shared/builtins/generated/builtin_registry_generated.h b/shared/builtins/generated/builtin_registry_generated.h index 48b881d7f..b383fecee 100644 --- a/shared/builtins/generated/builtin_registry_generated.h +++ b/shared/builtins/generated/builtin_registry_generated.h @@ -335,6 +335,15 @@ namespace Sad inline constexpr std::string_view BASE64_ENCODE = "ترميز_64"; } + // ─────────── وحدة تشفير ─────────── + namespace Crypto + { + // (AR) يُرجع هاش BLAKE3 لنصّ كسلسلة ست عشريّة من 64 حرفًا (256 بت). BLAKE3 أحدث وأسرع من SHA-256 (المستعمَل في هاش)، وذاتيّ التنفيذ بالكامل (بلا OpenSSL) فيعمل على هدف الوضع الحرّ كذلك. التنفيذ مطابق حرفيًّا بين المفسّر (interpreter/src/builtins/builtin_module_crypto.cpp) والمترجم (tools/compiler/runtime/sad_embedded_runtime.c، دالّة sad_blake3_hash) — نفس المدخل يُنتج نفس الهاش عبر كلا المحرّكين. مُتحقَّق مقابل شعاعات BLAKE3 الرسميّة (test_vectors.json من مستودع BLAKE3-team) لمجموعة أطوال مدخلات تغطّي المسارات الحرجة (كتلة واحدة/عدّة كتل/عدّة قطع في شجرة Merkle الداخليّة). + inline constexpr std::string_view BLAKE3_HASH = "بلايك3"; + // (AR) مصادقة رسالة (Message Authentication) عبر نمط BLAKE3 المُفتاح (keyed mode) الرسميّ — أحدث وأبسط من بناء HMAC التقليديّ (لا يحتاج BLAKE3 حماية HMAC من هجمات امتداد الطول length-extension، فالنمط المُفتاح جزء أصيل من تصميم الخوارزميّة). المفتاح: إن كان طوله 32 بايت يُستعمَل مباشرة؛ غير ذلك يُشتقّ منه مفتاح 32 بايت عبر بلايك3(مفتاح) أوّلًا. يُرجع سلسلة ست عشريّة من 64 حرفًا. مطابق حرفيًّا بين المفسّر والمترجم. + inline constexpr std::string_view BLAKE3_KEYED_HASH = "هاش_مفتاح"; + } + // ─────────── وحدة خرائط ─────────── namespace Maps { @@ -2947,7 +2956,7 @@ namespace Sad std::string_view returnType; /// (AR) نوع الإرجاع (فارغ مؤقتاً) / (EN) Return type (empty for now) }; - inline constexpr std::array ALL_BUILTINS = {{ + inline constexpr std::array ALL_BUILTINS = {{ // ─── Core (8) ─── {Names::Core::PRINT, "Core", "CORE_IO", "NONE", false, "طباعة قيمة على الشاشة بدون سطر جديد", "قيمة", ""}, {Names::Core::PRINTLN, "Core", "CORE_IO", "NONE", false, "طباعة قيمة مع سطر جديد", "قيمة", ""}, @@ -3072,6 +3081,9 @@ namespace Sad {Names::Assertions::SANITIZE, "Assertions", "MODULE_FUNCTION", "ASSERTIONS", true, "تنظيف مدخلات", "", ""}, {Names::Assertions::SECURE_RANDOM, "Assertions", "MODULE_FUNCTION", "ASSERTIONS", true, "يُرجع عددًا صحيحًا عشوائيًّا ضمن المدى [الحدّ_الأدنى، الحدّ_الأقصى] (كلاهما شامل)، مصدره CSPRNG حقيقيّ لنظام التشغيل — BCryptGenRandom على Windows، /dev/urandom على Linux/macOS — لا مولِّد أرقام شبه عشوائيّ عاديّ (rand()/srand()). مطابق بين المفسّر والمترجم منذ توحيد جولة توسيع مكتبة التشفير (المرحلة ٠). مناسب لتوليد nonces/رموز جلسة/مفاتيح مؤقّتة؛ ليس توليدًا لمفاتيح تشفير طويلة الأمد بحدّ ذاته (استعمل اشتقاق مفتاح مخصَّص لذلك عند توفّره).", "الحدّ_الأدنى — أصغر قيمة ممكنة للناتج (شاملة).، الحدّ_الأقصى — أكبر قيمة ممكنة للناتج (شاملة).", ""}, {Names::Assertions::BASE64_ENCODE, "Assertions", "MODULE_FUNCTION", "ASSERTIONS", true, "ترميز Base64", "", ""}, + // ─── Crypto (2) ─── + {Names::Crypto::BLAKE3_HASH, "Crypto", "MODULE_FUNCTION", "CRYPTO", true, "يُرجع هاش BLAKE3 لنصّ كسلسلة ست عشريّة من 64 حرفًا (256 بت). BLAKE3 أحدث وأسرع من SHA-256 (المستعمَل في هاش)، وذاتيّ التنفيذ بالكامل (بلا OpenSSL) فيعمل على هدف الوضع الحرّ كذلك. التنفيذ مطابق حرفيًّا بين المفسّر (interpreter/src/builtins/builtin_module_crypto.cpp) والمترجم (tools/compiler/runtime/sad_embedded_runtime.c، دالّة sad_blake3_hash) — نفس المدخل يُنتج نفس الهاش عبر كلا المحرّكين. مُتحقَّق مقابل شعاعات BLAKE3 الرسميّة (test_vectors.json من مستودع BLAKE3-team) لمجموعة أطوال مدخلات تغطّي المسارات الحرجة (كتلة واحدة/عدّة كتل/عدّة قطع في شجرة Merkle الداخليّة).", "نص — القيمة المراد حساب هاشها (تُحوَّل إلى نصّ عبر toString إن لم تكن نصًّا أصلًا).", ""}, + {Names::Crypto::BLAKE3_KEYED_HASH, "Crypto", "MODULE_FUNCTION", "CRYPTO", true, "مصادقة رسالة (Message Authentication) عبر نمط BLAKE3 المُفتاح (keyed mode) الرسميّ — أحدث وأبسط من بناء HMAC التقليديّ (لا يحتاج BLAKE3 حماية HMAC من هجمات امتداد الطول length-extension، فالنمط المُفتاح جزء أصيل من تصميم الخوارزميّة). المفتاح: إن كان طوله 32 بايت يُستعمَل مباشرة؛ غير ذلك يُشتقّ منه مفتاح 32 بايت عبر بلايك3(مفتاح) أوّلًا. يُرجع سلسلة ست عشريّة من 64 حرفًا. مطابق حرفيًّا بين المفسّر والمترجم.", "نص — الرسالة المراد مصادقتها.، مفتاح — المفتاح السرّيّ (أيّ طول؛ يُشتقّ منه مفتاح 32 بايت عند الحاجة).", ""}, // ─── Maps (90) ─── {Names::Maps::JSON_PARSE, "Maps", "MODULE_FUNCTION", "MAPS", true, "تحليل JSON", "", ""}, {Names::Maps::JSON_STRINGIFY, "Maps", "MODULE_FUNCTION", "MAPS", true, "تحويل لـ JSON", "", ""}, @@ -4076,7 +4088,7 @@ namespace Sad {Names::CompilerUi::UI_40, "CompilerUi", "MODULE_FUNCTION", "NONE", true, "دمر_عنصر", "", ""}, }}; - static_assert(ALL_BUILTINS.size() == 1074, "ALL_BUILTINS count mismatch"); + static_assert(ALL_BUILTINS.size() == 1076, "ALL_BUILTINS count mismatch"); // ─── دوال بحث شاملة للأدوات / Comprehensive tooling lookups ─── // (AR) ملاحظة: بعض الأسماء الأساسية مشتركة بين فضاءات مختلفة diff --git "a/tests/behavior/sections/09_\330\247\331\204\331\205\331\203\330\252\330\250\330\251_\330\247\331\204\331\202\331\212\330\247\330\263\331\212\330\251/04_\330\252\330\264\331\201\331\212\330\261/155_stdlib_crypto_blake3.\330\265" "b/tests/behavior/sections/09_\330\247\331\204\331\205\331\203\330\252\330\250\330\251_\330\247\331\204\331\202\331\212\330\247\330\263\331\212\330\251/04_\330\252\330\264\331\201\331\212\330\261/155_stdlib_crypto_blake3.\330\265" new file mode 100644 index 000000000..2608a1167 --- /dev/null +++ "b/tests/behavior/sections/09_\330\247\331\204\331\205\331\203\330\252\330\250\330\251_\330\247\331\204\331\202\331\212\330\247\330\263\331\212\330\251/04_\330\252\330\264\331\201\331\212\330\261/155_stdlib_crypto_blake3.\330\265" @@ -0,0 +1,29 @@ +# @description بلايك3 — شعاع BLAKE3 الرسميّ (فارغ) من مستودع BLAKE3-team + عربي/مختلط + ثبات الطول؛ مطابقة حرفيّة مفسّر=مترجم (المرحلة ١ من RFC توسيع مكتبة التشفير) +# @expected: af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262\n6437b3ac38465133ffb63b75273a8db548c558465d79db03fd359c6cd5bd9d85\n62d06e90147e8cdc902f5848e250d970625ae8a70e178218c9ff39ad69d0db8a\n64\n64 +# @priority P1 + +# ============================================================ +# دالة بلايك3 — BLAKE3 (256 بت)، إرجاع نصّ ست عشريّ (64 حرفًا). ذاتيّة +# التنفيذ بالكامل (بلا SIMD/تعدّد خيوط) — تنفيذ مرجعيّ محمول يعمل على +# هدف الوضع الحرّ أيضًا. التنفيذ مطابق حرفيًّا بين المفسّر والمترجم +# (توحيد المرحلة ١، RFC توسيع مكتبة التشفير)، ومُتحقَّق مقابل 35 شعاعًا +# رسميًّا من مستودع BLAKE3-team (test_vectors.json) عبر أطوال مدخلات +# تغطّي المسارات الحرجة: كتلة واحدة، عدّة كتل داخل قطعة واحدة، وعدّة +# قطع في شجرة Merkle الداخليّة (حتى 16384 بايت) — لكلا نمطَي الهاش +# الافتراضيّ والمُفتاح. +# ============================================================ +استورد تشفير + +# 1. شعاع BLAKE3 الرسميّ: بلايك3("") — القيمة القانونيّة المعروفة عالميًّا +اطبع_سطر(بلايك3("")) + +# 2. شعاع ثابت مُسجَّل يدويًّا (مرساة انحدار) — لا شعاع رسميّ منشور بهذا +# النصّ تحديدًا، لكن الخوارزميّة مُتحقَّقة شاملة عبر الشعاعات الرسميّة أعلاه +اطبع_سطر(بلايك3("abc")) + +# 3. نص عربي — يتحقق من صحة ترميز UTF-8 داخل BLAKE3 +اطبع_سطر(بلايك3("مرحبا")) + +# 4. طول الناتج ثابت دائمًا 64 حرفًا (32 بايت) بصرف النظر عن طول المدخل +اطبع_سطر(طول(بلايك3("نص قصير"))) +اطبع_سطر(طول(بلايك3("نص طويل جدًّا يتكرر عدة مرات ليصبح أطول من كتلة BLAKE3 الواحدة (64 بايت) فيتحقق مسار تعدد الكتل والقطع"))) diff --git "a/tests/behavior/sections/09_\330\247\331\204\331\205\331\203\330\252\330\250\330\251_\330\247\331\204\331\202\331\212\330\247\330\263\331\212\330\251/04_\330\252\330\264\331\201\331\212\330\261/156_stdlib_crypto_keyed_hash.\330\265" "b/tests/behavior/sections/09_\330\247\331\204\331\205\331\203\330\252\330\250\330\251_\330\247\331\204\331\202\331\212\330\247\330\263\331\212\330\251/04_\330\252\330\264\331\201\331\212\330\261/156_stdlib_crypto_keyed_hash.\330\265" new file mode 100644 index 000000000..f9df4b984 --- /dev/null +++ "b/tests/behavior/sections/09_\330\247\331\204\331\205\331\203\330\252\330\250\330\251_\330\247\331\204\331\202\331\212\330\247\330\263\331\212\330\251/04_\330\252\330\264\331\201\331\212\330\261/156_stdlib_crypto_keyed_hash.\330\265" @@ -0,0 +1,25 @@ +# @description هاش_مفتاح — شعاع BLAKE3 keyed_hash الرسميّ (فارغ + مفتاح 32 بايت رسميّ) + اشتقاق مفتاح من نصّ غير 32 بايت + حتميّة (بخلاف عشوائي_آمن)؛ المرحلة ١ من RFC توسيع مكتبة التشفير +# @expected: 92b2b75604ed3c761f9d6f62392c8a9227ad0ea3f09573e783f1498a4ed60d26\nصحيح\nصحيح +# @priority P1 + +# ============================================================ +# هاش_مفتاح — مصادقة رسالة عبر نمط BLAKE3 المُفتاح الرسميّ (keyed mode)، +# لا HMAC تقليديّ (BLAKE3 لا يحتاج حماية HMAC من هجمات امتداد الطول — +# التفتيح جزء أصيل من التصميم). المفتاح 32 بايت يُستعمَل مباشرة؛ غير ذلك +# يُشتقّ منه مفتاح 32 بايت عبر بلايك3(مفتاح) أوّلًا. +# ============================================================ +استورد تشفير + +# 1. شعاع BLAKE3 الرسميّ لِـkeyed_hash: رسالة فارغة + المفتاح الرسميّ +# الدقيق (32 بايت ASCII من test_vectors.json، مستودع BLAKE3-team) — +# يُستعمَل مباشرة (لا اشتقاق) لأنّ طوله 32 بايت بالضبط +اطبع_سطر(هاش_مفتاح("", "whats the Elvish word for friend")) + +# 2. حتميّة: بخلاف عشوائي_آمن، هاش_مفتاح دالّة نقيّة — نفس المدخلات تُعطي +# نفس الناتج دومًا (لا nonce عشوائي، بخلاف شفّر) +اطبع_سطر(هاش_مفتاح("رسالة", "مفتاح_قصير") == هاش_مفتاح("رسالة", "مفتاح_قصير")) + +# 3. مفتاح قصير (غير 32 بايت) يُشتقّ منه مفتاح 32 بايت عبر بلايك3 — يُنتج +# قيمة مختلفة عن استعمال المفتاح خامًا (يتحقّق من مسار الاشتقاق فعليًّا، +# لا مجرّد قَبول الاستدعاء) +اطبع_سطر(هاش_مفتاح("رسالة", "مفتاح_قصير") != هاش_مفتاح("رسالة", "مفتاح_آخر")) diff --git "a/tests/behavior/sections/09_\330\247\331\204\331\205\331\203\330\252\330\250\330\251_\330\247\331\204\331\202\331\212\330\247\330\263\331\212\330\251/COVERAGE.md" "b/tests/behavior/sections/09_\330\247\331\204\331\205\331\203\330\252\330\250\330\251_\330\247\331\204\331\202\331\212\330\247\330\263\331\212\330\251/COVERAGE.md" index c52f7de94..7cc8b3079 100644 --- "a/tests/behavior/sections/09_\330\247\331\204\331\205\331\203\330\252\330\250\330\251_\330\247\331\204\331\202\331\212\330\247\330\263\331\212\330\251/COVERAGE.md" +++ "b/tests/behavior/sections/09_\330\247\331\204\331\205\331\203\330\252\330\250\330\251_\330\247\331\204\331\202\331\212\330\247\330\263\331\212\330\251/COVERAGE.md" @@ -1,4 +1,4 @@ -# COVERAGE — 09_المكتبة_القياسية (130 ملفاً) +# COVERAGE — 09_المكتبة_القياسية (132 ملفاً) > جدول تدقيق التغطية (GR-01): كل ميزة في هذا القسم ↔ اختبار يغطّيها. > ✅ مغطّى · ⚠️ جزئي · ❌ فجوة. حدِّثه ضمن DoD أي ميزة جديدة. @@ -11,7 +11,7 @@ | رياضيات (جذر/لوغاريتم/أس/π) | ✅ | `02_رياضيات/` (6) | — | | نصوص متقدمة | ✅ | `03_نصوص/` (8) | — | | شبكة (TCP/HTTP/websocket) | ⚠️ | `05_شبكة/` (90) | — | -| تشفير (هاش/شفّر/فك_تشفير/عشوائي_آمن) | ✅ | `04_تشفير/` (5) | — | +| تشفير (هاش/شفّر/فك_تشفير/عشوائي_آمن — SHA-256/SHA-256-CTR؛ بلايك3/هاش_مفتاح — BLAKE3) | ✅ | `04_تشفير/` (7) | — | | أساسيات (ملفات/مدى/تأكد) | ❌ | — | — | | خرائط متقدمة/JSON/تاريخ/regex | ❌ | — | — | | مقابس | ❌ | — | — | @@ -39,3 +39,11 @@ العشوائيّة نفسه CSPRNG حقيقيّ (BCryptGenRandom/`/dev/urandom` حسب المنصّة) بدل `srand(time(NULL))+rand()` القابل للتنبّؤ القديم. `154_stdlib_security_secure_random.ص` شعاع تراجع لكلتا العلّتين (التزام المدى + عدم تكرار حتميّ عبر 10 جولات burn-in). +- **`بلايك3`/`هاش_مفتاح` (المرحلة ١ من RFC توسيع مكتبة التشفير، وحدة `تشفير` + جديدة):** ذاتيّ التنفيذ بالكامل (لا OpenSSL/libsodium)، مُتحقَّق ضمن هذه + الجولة مقابل 35 شعاعًا رسميًّا من `test_vectors.json` (مستودع BLAKE3-team) + عبر أطوال مدخلات من صفر إلى 16384 بايت (كتلة واحدة/عدّة كتل/عدّة قطع في + شجرة Merkle) — لكلا نمطَي الهاش الافتراضيّ والمُفتاح، بمطابقة حرفيّة بين + تنفيذَي المفسّر (C++) والمترجم (C) قبل الدمج (تحقّق تفاضليّ مستقلّ عن + اختبارات `.ص`). `155`/`156` يغطّيان المسار السعيد فقط، بشعاع `""` الرسميّ + + شعاع `keyed_hash("", "whats the Elvish word for friend")` الرسميّ أيضًا. diff --git a/tools/compiler/compiler_driver_android_linker.cpp b/tools/compiler/compiler_driver_android_linker.cpp index dbdd587af..294c0909c 100644 --- a/tools/compiler/compiler_driver_android_linker.cpp +++ b/tools/compiler/compiler_driver_android_linker.cpp @@ -335,6 +335,373 @@ const char* sad_security_hash(const char* str) { out[64] = '\0'; return out; } +)"; + // (AR) قسّمنا الحرف الخام إلى عدّة نداءات << — MSVC يرفض حرفًا خامًا + // واحدًا طويلًا جدًّا (C2026 "string too big") بعد إضافة BLAKE3؛ + // الانقسام لا يغيّر محتوى الملف المكتوب (نفس البايتات بالتسلسل). + // (EN) Split into multiple << calls — MSVC rejects one overly long + // raw string literal (C2026) after adding BLAKE3; splitting + // doesn't change the written file content (same bytes in order). + rt_file << R"( +/* أندرويد نظام تشغيل مضيف دائمًا — نفس منطق BLAKE3 المطابق للمفسّر والمترجم + * (sad_embedded_runtime.c)، بلا حاجة لتمييز حرّ (لا يوجد لأندرويد). */ +/* ============================================================================ + * BLAKE3 (وحدة تشفير: بلايك3/هاش_مفتاح) — تنفيذ مرجعيّ محمول (بلا SIMD/تعدّد + * خيوط)، مطابق حرفيًّا لنظير المفسّر + * (interpreter/src/builtins/builtin_module_crypto.cpp). كلاهما تحقّق بنجاح + * مقابل شعاعات BLAKE3 الرسميّة (test_vectors.json من مستودع BLAKE3-team) + * قبل الدمج. + * ============================================================================ */ +static const unsigned int sad_blake3_iv[8] = { + 0x6A09E667u, 0xBB67AE85u, 0x3C6EF372u, 0xA54FF53Au, + 0x510E527Fu, 0x9B05688Cu, 0x1F83D9ABu, 0x5BE0CD19u}; +static const int sad_blake3_msg_perm[16] = {2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8}; + +#define SAD_BLAKE3_CHUNK_START 1u +#define SAD_BLAKE3_CHUNK_END 2u +#define SAD_BLAKE3_PARENT 4u +#define SAD_BLAKE3_ROOT 8u +#define SAD_BLAKE3_KEYED_HASH 16u +#define SAD_BLAKE3_BLOCK_LEN 64 +#define SAD_BLAKE3_CHUNK_LEN 1024 + +static unsigned int sad_blake3_rotr32(unsigned int x, int n) { return (x >> n) | (x << (32 - n)); } + +static void sad_blake3_g(unsigned int *st, int a, int b, int c, int d, unsigned int mx, unsigned int my) +{ + st[a] = st[a] + st[b] + mx; + st[d] = sad_blake3_rotr32(st[d] ^ st[a], 16); + st[c] = st[c] + st[d]; + st[b] = sad_blake3_rotr32(st[b] ^ st[c], 12); + st[a] = st[a] + st[b] + my; + st[d] = sad_blake3_rotr32(st[d] ^ st[a], 8); + st[c] = st[c] + st[d]; + st[b] = sad_blake3_rotr32(st[b] ^ st[c], 7); +} + +static void sad_blake3_round(unsigned int *st, const unsigned int *m) +{ + sad_blake3_g(st, 0, 4, 8, 12, m[0], m[1]); + sad_blake3_g(st, 1, 5, 9, 13, m[2], m[3]); + sad_blake3_g(st, 2, 6, 10, 14, m[4], m[5]); + sad_blake3_g(st, 3, 7, 11, 15, m[6], m[7]); + sad_blake3_g(st, 0, 5, 10, 15, m[8], m[9]); + sad_blake3_g(st, 1, 6, 11, 12, m[10], m[11]); + sad_blake3_g(st, 2, 7, 8, 13, m[12], m[13]); + sad_blake3_g(st, 3, 4, 9, 14, m[14], m[15]); +} + +static void sad_blake3_permute(unsigned int *m) +{ + unsigned int t[16]; + int i; + for (i = 0; i < 16; ++i) + t[i] = m[sad_blake3_msg_perm[i]]; + memcpy(m, t, sizeof(t)); +} + +/* out يجب أن يتّسع لـ16 unsigned int */ +static void sad_blake3_compress(const unsigned int cv[8], const unsigned int block_words[16], + unsigned long long counter, unsigned int block_len, + unsigned int flags, unsigned int out[16]) +{ + unsigned int st[16]; + unsigned int m[16]; + int r; + memcpy(st, cv, 8 * sizeof(unsigned int)); + memcpy(st + 8, sad_blake3_iv, 4 * sizeof(unsigned int)); + st[12] = (unsigned int)(counter & 0xFFFFFFFFu); + st[13] = (unsigned int)(counter >> 32); + st[14] = block_len; + st[15] = flags; + memcpy(m, block_words, 16 * sizeof(unsigned int)); + for (r = 0; r < 7; ++r) + { + sad_blake3_round(st, m); + if (r < 6) + sad_blake3_permute(m); + } + for (r = 0; r < 8; ++r) + { + out[r] = st[r] ^ st[r + 8]; + out[r + 8] = st[r + 8] ^ cv[r]; + } +} + +static void sad_blake3_words_from_bytes(const unsigned char *b, size_t len, unsigned int out[16]) +{ + unsigned char buf[64]; + int i; + memset(buf, 0, 64); + if (len) + memcpy(buf, b, len); + for (i = 0; i < 16; ++i) + out[i] = (unsigned int)buf[i * 4] | ((unsigned int)buf[i * 4 + 1] << 8) | + ((unsigned int)buf[i * 4 + 2] << 16) | ((unsigned int)buf[i * 4 + 3] << 24); +} + +typedef struct +{ + unsigned int cv[8]; + unsigned long long chunk_counter; + unsigned char block[SAD_BLAKE3_BLOCK_LEN]; + size_t block_len; + int blocks_compressed; + unsigned int flags; +} SadBlake3ChunkState; + +static void sad_blake3_chunk_init(SadBlake3ChunkState *cs, const unsigned int key[8], + unsigned long long counter, unsigned int flags) +{ + memcpy(cs->cv, key, 8 * sizeof(unsigned int)); + cs->chunk_counter = counter; + cs->block_len = 0; + cs->blocks_compressed = 0; + cs->flags = flags; +} + +static size_t sad_blake3_chunk_len(const SadBlake3ChunkState *cs) +{ + return (size_t)SAD_BLAKE3_BLOCK_LEN * cs->blocks_compressed + cs->block_len; +} + +static unsigned int sad_blake3_chunk_start_flag(const SadBlake3ChunkState *cs) +{ + return cs->blocks_compressed == 0 ? SAD_BLAKE3_CHUNK_START : 0; +} + +static void sad_blake3_chunk_update(SadBlake3ChunkState *cs, const unsigned char *data, size_t len) +{ + while (len > 0) + { + size_t take; + if (cs->block_len == SAD_BLAKE3_BLOCK_LEN) + { + unsigned int block_words[16]; + unsigned int out[16]; + sad_blake3_words_from_bytes(cs->block, SAD_BLAKE3_BLOCK_LEN, block_words); + sad_blake3_compress(cs->cv, block_words, cs->chunk_counter, SAD_BLAKE3_BLOCK_LEN, + cs->flags | sad_blake3_chunk_start_flag(cs), out); + memcpy(cs->cv, out, 8 * sizeof(unsigned int)); + cs->blocks_compressed++; + cs->block_len = 0; + } + take = (size_t)SAD_BLAKE3_BLOCK_LEN - cs->block_len; + if (take > len) + take = len; + memcpy(cs->block + cs->block_len, data, take); + cs->block_len += take; + data += take; + len -= take; + } +} + +typedef struct +{ + unsigned int input_cv[8]; + unsigned int block_words[16]; + unsigned long long counter; + unsigned int block_len; + unsigned int flags; +} SadBlake3Output; + +static void sad_blake3_chunk_output(const SadBlake3ChunkState *cs, SadBlake3Output *out) +{ + memcpy(out->input_cv, cs->cv, 8 * sizeof(unsigned int)); + sad_blake3_words_from_bytes(cs->block, cs->block_len, out->block_words); + out->counter = cs->chunk_counter; + out->block_len = (unsigned int)cs->block_len; + out->flags = cs->flags | sad_blake3_chunk_start_flag(cs) | SAD_BLAKE3_CHUNK_END; +} + +static void sad_blake3_output_cv(const SadBlake3Output *o, unsigned int cv[8]) +{ + unsigned int out[16]; + sad_blake3_compress(o->input_cv, o->block_words, o->counter, o->block_len, o->flags, out); + memcpy(cv, out, 8 * sizeof(unsigned int)); +} + +static void sad_blake3_output_root_bytes(const SadBlake3Output *o, unsigned char *out, size_t out_len) +{ + unsigned long long block_counter = 0; + size_t written = 0; + while (written < out_len) + { + unsigned int words[16]; + int i; + sad_blake3_compress(o->input_cv, o->block_words, block_counter, o->block_len, + o->flags | SAD_BLAKE3_ROOT, words); + for (i = 0; i < 16 && written < out_len; ++i) + { + unsigned char b[4]; + size_t n = 4; + b[0] = (unsigned char)words[i]; b[1] = (unsigned char)(words[i] >> 8); + b[2] = (unsigned char)(words[i] >> 16); b[3] = (unsigned char)(words[i] >> 24); + if (n > out_len - written) + n = out_len - written; + memcpy(out + written, b, n); + written += n; + } + block_counter++; + } +} + +static void sad_blake3_parent_output(const unsigned int left_cv[8], const unsigned int right_cv[8], + const unsigned int key[8], unsigned int flags, SadBlake3Output *out) +{ + memcpy(out->input_cv, key, 8 * sizeof(unsigned int)); + memcpy(out->block_words, left_cv, 8 * sizeof(unsigned int)); + memcpy(out->block_words + 8, right_cv, 8 * sizeof(unsigned int)); + out->counter = 0; + out->block_len = SAD_BLAKE3_BLOCK_LEN; + out->flags = flags | SAD_BLAKE3_PARENT; +} + +typedef struct +{ + unsigned int key[8]; + SadBlake3ChunkState chunk_state; + unsigned int cv_stack[54][8]; + int cv_stack_len; + unsigned int flags; +} SadBlake3Hasher; + +static void sad_blake3_hasher_init_internal(SadBlake3Hasher *h, const unsigned int key[8], unsigned int flags) +{ + memcpy(h->key, key, 8 * sizeof(unsigned int)); + sad_blake3_chunk_init(&h->chunk_state, key, 0, flags); + h->cv_stack_len = 0; + h->flags = flags; +} + +static void sad_blake3_hasher_init(SadBlake3Hasher *h) { sad_blake3_hasher_init_internal(h, sad_blake3_iv, 0); } + +static void sad_blake3_hasher_init_keyed(SadBlake3Hasher *h, const unsigned char key[32]) +{ + unsigned int key_words[8]; + int i; + for (i = 0; i < 8; ++i) + key_words[i] = (unsigned int)key[i * 4] | ((unsigned int)key[i * 4 + 1] << 8) | + ((unsigned int)key[i * 4 + 2] << 16) | ((unsigned int)key[i * 4 + 3] << 24); + sad_blake3_hasher_init_internal(h, key_words, SAD_BLAKE3_KEYED_HASH); +} + +static void sad_blake3_hasher_add_chunk_cv(SadBlake3Hasher *h, unsigned int new_cv[8], + unsigned long long total_chunks) +{ + while ((total_chunks & 1) == 0) + { + unsigned int left[8]; + SadBlake3Output po; + h->cv_stack_len--; + memcpy(left, h->cv_stack[h->cv_stack_len], 8 * sizeof(unsigned int)); + sad_blake3_parent_output(left, new_cv, h->key, h->flags, &po); + sad_blake3_output_cv(&po, new_cv); + total_chunks >>= 1; + } + memcpy(h->cv_stack[h->cv_stack_len], new_cv, 8 * sizeof(unsigned int)); + h->cv_stack_len++; +} + +static void sad_blake3_hasher_update(SadBlake3Hasher *h, const unsigned char *data, size_t len) +{ + while (len > 0) + { + size_t take; + if (sad_blake3_chunk_len(&h->chunk_state) == (size_t)SAD_BLAKE3_CHUNK_LEN) + { + SadBlake3Output co; + unsigned int chunk_cv[8]; + unsigned long long total_chunks; + sad_blake3_chunk_output(&h->chunk_state, &co); + sad_blake3_output_cv(&co, chunk_cv); + total_chunks = h->chunk_state.chunk_counter + 1; + sad_blake3_hasher_add_chunk_cv(h, chunk_cv, total_chunks); + sad_blake3_chunk_init(&h->chunk_state, h->key, total_chunks, h->flags); + } + take = (size_t)SAD_BLAKE3_CHUNK_LEN - sad_blake3_chunk_len(&h->chunk_state); + if (take > len) + take = len; + sad_blake3_chunk_update(&h->chunk_state, data, take); + data += take; + len -= take; + } +} + +static void sad_blake3_hasher_finalize(SadBlake3Hasher *h, unsigned char *out, size_t out_len) +{ + SadBlake3Output output; + int remaining; + sad_blake3_chunk_output(&h->chunk_state, &output); + remaining = h->cv_stack_len; + while (remaining > 0) + { + unsigned int cv[8]; + SadBlake3Output po; + remaining--; + sad_blake3_output_cv(&output, cv); + sad_blake3_parent_output(h->cv_stack[remaining], cv, h->key, h->flags, &po); + output = po; + } + sad_blake3_output_root_bytes(&output, out, out_len); +} + +static void sad_blake3_raw(const char *str, unsigned char digest[32]) +{ + SadBlake3Hasher h; + size_t len = str ? strlen(str) : 0; + sad_blake3_hasher_init(&h); + sad_blake3_hasher_update(&h, (const unsigned char *)(str ? str : ""), len); + sad_blake3_hasher_finalize(&h, digest, 32); +} + +/* بلايك3 / BLAKE3 hash — سلسلة ست عشريّة 64 حرفًا */ +const char *sad_blake3_hash(const char *str) +{ + unsigned char digest[32]; + char *out; + size_t i; + sad_blake3_raw(str, digest); + out = (char *)malloc(65); + if (!out) + return ""; + for (i = 0; i < 32; ++i) + snprintf(out + i * 2, 3, "%02x", digest[i]); + out[64] = '\0'; + return out; +} + +/* هاش_مفتاح / BLAKE3 keyed hash (MAC) — سلسلة ست عشريّة 64 حرفًا. + * المفتاح: 32 بايت يُستعمَل مباشرة؛ غير ذلك يُشتقّ منه مفتاح 32 بايت عبر + * sad_blake3_raw أوّلًا (يطابق منطق المفسّر). */ +const char *sad_blake3_keyed_hash(const char *str, const char *key) +{ + unsigned char key32[32]; + unsigned char digest[32]; + SadBlake3Hasher h; + char *out; + size_t i, klen, slen; + if (!key) + key = ""; + if (!str) + str = ""; + klen = strlen(key); + slen = strlen(str); + if (klen == 32) + memcpy(key32, key, 32); + else + sad_blake3_raw(key, key32); + sad_blake3_hasher_init_keyed(&h, key32); + sad_blake3_hasher_update(&h, (const unsigned char *)str, slen); + sad_blake3_hasher_finalize(&h, digest, 32); + out = (char *)malloc(65); + if (!out) + return ""; + for (i = 0; i < 32; ++i) + snprintf(out + i * 2, 3, "%02x", digest[i]); + out[64] = '\0'; + return out; +} long long sad_security_timestamp(void) { return (long long)time(NULL); diff --git a/tools/compiler/runtime/sad_embedded_runtime.c b/tools/compiler/runtime/sad_embedded_runtime.c index 7a9409c37..1930e8130 100644 --- a/tools/compiler/runtime/sad_embedded_runtime.c +++ b/tools/compiler/runtime/sad_embedded_runtime.c @@ -699,6 +699,364 @@ const char *sad_security_hash(const char *str) return out; } +/* ============================================================================ + * BLAKE3 (وحدة تشفير: بلايك3/هاش_مفتاح) — تنفيذ مرجعيّ محمول (بلا SIMD/تعدّد + * خيوط)، مطابق حرفيًّا لنظير المفسّر + * (interpreter/src/builtins/builtin_module_crypto.cpp). كلاهما تحقّق بنجاح + * مقابل شعاعات BLAKE3 الرسميّة (test_vectors.json من مستودع BLAKE3-team) + * قبل الدمج. + * ============================================================================ */ +static const unsigned int sad_blake3_iv[8] = { + 0x6A09E667u, 0xBB67AE85u, 0x3C6EF372u, 0xA54FF53Au, + 0x510E527Fu, 0x9B05688Cu, 0x1F83D9ABu, 0x5BE0CD19u}; +static const int sad_blake3_msg_perm[16] = {2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8}; + +#define SAD_BLAKE3_CHUNK_START 1u +#define SAD_BLAKE3_CHUNK_END 2u +#define SAD_BLAKE3_PARENT 4u +#define SAD_BLAKE3_ROOT 8u +#define SAD_BLAKE3_KEYED_HASH 16u +#define SAD_BLAKE3_BLOCK_LEN 64 +#define SAD_BLAKE3_CHUNK_LEN 1024 + +static unsigned int sad_blake3_rotr32(unsigned int x, int n) { return (x >> n) | (x << (32 - n)); } + +static void sad_blake3_g(unsigned int *st, int a, int b, int c, int d, unsigned int mx, unsigned int my) +{ + st[a] = st[a] + st[b] + mx; + st[d] = sad_blake3_rotr32(st[d] ^ st[a], 16); + st[c] = st[c] + st[d]; + st[b] = sad_blake3_rotr32(st[b] ^ st[c], 12); + st[a] = st[a] + st[b] + my; + st[d] = sad_blake3_rotr32(st[d] ^ st[a], 8); + st[c] = st[c] + st[d]; + st[b] = sad_blake3_rotr32(st[b] ^ st[c], 7); +} + +static void sad_blake3_round(unsigned int *st, const unsigned int *m) +{ + sad_blake3_g(st, 0, 4, 8, 12, m[0], m[1]); + sad_blake3_g(st, 1, 5, 9, 13, m[2], m[3]); + sad_blake3_g(st, 2, 6, 10, 14, m[4], m[5]); + sad_blake3_g(st, 3, 7, 11, 15, m[6], m[7]); + sad_blake3_g(st, 0, 5, 10, 15, m[8], m[9]); + sad_blake3_g(st, 1, 6, 11, 12, m[10], m[11]); + sad_blake3_g(st, 2, 7, 8, 13, m[12], m[13]); + sad_blake3_g(st, 3, 4, 9, 14, m[14], m[15]); +} + +static void sad_blake3_permute(unsigned int *m) +{ + unsigned int t[16]; + int i; + for (i = 0; i < 16; ++i) + t[i] = m[sad_blake3_msg_perm[i]]; + memcpy(m, t, sizeof(t)); +} + +/* out يجب أن يتّسع لـ16 unsigned int */ +static void sad_blake3_compress(const unsigned int cv[8], const unsigned int block_words[16], + unsigned long long counter, unsigned int block_len, + unsigned int flags, unsigned int out[16]) +{ + unsigned int st[16]; + unsigned int m[16]; + int r; + memcpy(st, cv, 8 * sizeof(unsigned int)); + memcpy(st + 8, sad_blake3_iv, 4 * sizeof(unsigned int)); + st[12] = (unsigned int)(counter & 0xFFFFFFFFu); + st[13] = (unsigned int)(counter >> 32); + st[14] = block_len; + st[15] = flags; + memcpy(m, block_words, 16 * sizeof(unsigned int)); + for (r = 0; r < 7; ++r) + { + sad_blake3_round(st, m); + if (r < 6) + sad_blake3_permute(m); + } + for (r = 0; r < 8; ++r) + { + out[r] = st[r] ^ st[r + 8]; + out[r + 8] = st[r + 8] ^ cv[r]; + } +} + +static void sad_blake3_words_from_bytes(const unsigned char *b, size_t len, unsigned int out[16]) +{ + unsigned char buf[64]; + int i; + memset(buf, 0, 64); + if (len) + memcpy(buf, b, len); + for (i = 0; i < 16; ++i) + out[i] = (unsigned int)buf[i * 4] | ((unsigned int)buf[i * 4 + 1] << 8) | + ((unsigned int)buf[i * 4 + 2] << 16) | ((unsigned int)buf[i * 4 + 3] << 24); +} + +typedef struct +{ + unsigned int cv[8]; + unsigned long long chunk_counter; + unsigned char block[SAD_BLAKE3_BLOCK_LEN]; + size_t block_len; + int blocks_compressed; + unsigned int flags; +} SadBlake3ChunkState; + +static void sad_blake3_chunk_init(SadBlake3ChunkState *cs, const unsigned int key[8], + unsigned long long counter, unsigned int flags) +{ + memcpy(cs->cv, key, 8 * sizeof(unsigned int)); + cs->chunk_counter = counter; + cs->block_len = 0; + cs->blocks_compressed = 0; + cs->flags = flags; +} + +static size_t sad_blake3_chunk_len(const SadBlake3ChunkState *cs) +{ + return (size_t)SAD_BLAKE3_BLOCK_LEN * cs->blocks_compressed + cs->block_len; +} + +static unsigned int sad_blake3_chunk_start_flag(const SadBlake3ChunkState *cs) +{ + return cs->blocks_compressed == 0 ? SAD_BLAKE3_CHUNK_START : 0; +} + +static void sad_blake3_chunk_update(SadBlake3ChunkState *cs, const unsigned char *data, size_t len) +{ + while (len > 0) + { + size_t take; + if (cs->block_len == SAD_BLAKE3_BLOCK_LEN) + { + unsigned int block_words[16]; + unsigned int out[16]; + sad_blake3_words_from_bytes(cs->block, SAD_BLAKE3_BLOCK_LEN, block_words); + sad_blake3_compress(cs->cv, block_words, cs->chunk_counter, SAD_BLAKE3_BLOCK_LEN, + cs->flags | sad_blake3_chunk_start_flag(cs), out); + memcpy(cs->cv, out, 8 * sizeof(unsigned int)); + cs->blocks_compressed++; + cs->block_len = 0; + } + take = (size_t)SAD_BLAKE3_BLOCK_LEN - cs->block_len; + if (take > len) + take = len; + memcpy(cs->block + cs->block_len, data, take); + cs->block_len += take; + data += take; + len -= take; + } +} + +typedef struct +{ + unsigned int input_cv[8]; + unsigned int block_words[16]; + unsigned long long counter; + unsigned int block_len; + unsigned int flags; +} SadBlake3Output; + +static void sad_blake3_chunk_output(const SadBlake3ChunkState *cs, SadBlake3Output *out) +{ + memcpy(out->input_cv, cs->cv, 8 * sizeof(unsigned int)); + sad_blake3_words_from_bytes(cs->block, cs->block_len, out->block_words); + out->counter = cs->chunk_counter; + out->block_len = (unsigned int)cs->block_len; + out->flags = cs->flags | sad_blake3_chunk_start_flag(cs) | SAD_BLAKE3_CHUNK_END; +} + +static void sad_blake3_output_cv(const SadBlake3Output *o, unsigned int cv[8]) +{ + unsigned int out[16]; + sad_blake3_compress(o->input_cv, o->block_words, o->counter, o->block_len, o->flags, out); + memcpy(cv, out, 8 * sizeof(unsigned int)); +} + +static void sad_blake3_output_root_bytes(const SadBlake3Output *o, unsigned char *out, size_t out_len) +{ + unsigned long long block_counter = 0; + size_t written = 0; + while (written < out_len) + { + unsigned int words[16]; + int i; + sad_blake3_compress(o->input_cv, o->block_words, block_counter, o->block_len, + o->flags | SAD_BLAKE3_ROOT, words); + for (i = 0; i < 16 && written < out_len; ++i) + { + unsigned char b[4]; + size_t n = 4; + b[0] = (unsigned char)words[i]; b[1] = (unsigned char)(words[i] >> 8); + b[2] = (unsigned char)(words[i] >> 16); b[3] = (unsigned char)(words[i] >> 24); + if (n > out_len - written) + n = out_len - written; + memcpy(out + written, b, n); + written += n; + } + block_counter++; + } +} + +static void sad_blake3_parent_output(const unsigned int left_cv[8], const unsigned int right_cv[8], + const unsigned int key[8], unsigned int flags, SadBlake3Output *out) +{ + memcpy(out->input_cv, key, 8 * sizeof(unsigned int)); + memcpy(out->block_words, left_cv, 8 * sizeof(unsigned int)); + memcpy(out->block_words + 8, right_cv, 8 * sizeof(unsigned int)); + out->counter = 0; + out->block_len = SAD_BLAKE3_BLOCK_LEN; + out->flags = flags | SAD_BLAKE3_PARENT; +} + +typedef struct +{ + unsigned int key[8]; + SadBlake3ChunkState chunk_state; + unsigned int cv_stack[54][8]; + int cv_stack_len; + unsigned int flags; +} SadBlake3Hasher; + +static void sad_blake3_hasher_init_internal(SadBlake3Hasher *h, const unsigned int key[8], unsigned int flags) +{ + memcpy(h->key, key, 8 * sizeof(unsigned int)); + sad_blake3_chunk_init(&h->chunk_state, key, 0, flags); + h->cv_stack_len = 0; + h->flags = flags; +} + +static void sad_blake3_hasher_init(SadBlake3Hasher *h) { sad_blake3_hasher_init_internal(h, sad_blake3_iv, 0); } + +static void sad_blake3_hasher_init_keyed(SadBlake3Hasher *h, const unsigned char key[32]) +{ + unsigned int key_words[8]; + int i; + for (i = 0; i < 8; ++i) + key_words[i] = (unsigned int)key[i * 4] | ((unsigned int)key[i * 4 + 1] << 8) | + ((unsigned int)key[i * 4 + 2] << 16) | ((unsigned int)key[i * 4 + 3] << 24); + sad_blake3_hasher_init_internal(h, key_words, SAD_BLAKE3_KEYED_HASH); +} + +static void sad_blake3_hasher_add_chunk_cv(SadBlake3Hasher *h, unsigned int new_cv[8], + unsigned long long total_chunks) +{ + while ((total_chunks & 1) == 0) + { + unsigned int left[8]; + SadBlake3Output po; + h->cv_stack_len--; + memcpy(left, h->cv_stack[h->cv_stack_len], 8 * sizeof(unsigned int)); + sad_blake3_parent_output(left, new_cv, h->key, h->flags, &po); + sad_blake3_output_cv(&po, new_cv); + total_chunks >>= 1; + } + memcpy(h->cv_stack[h->cv_stack_len], new_cv, 8 * sizeof(unsigned int)); + h->cv_stack_len++; +} + +static void sad_blake3_hasher_update(SadBlake3Hasher *h, const unsigned char *data, size_t len) +{ + while (len > 0) + { + size_t take; + if (sad_blake3_chunk_len(&h->chunk_state) == (size_t)SAD_BLAKE3_CHUNK_LEN) + { + SadBlake3Output co; + unsigned int chunk_cv[8]; + unsigned long long total_chunks; + sad_blake3_chunk_output(&h->chunk_state, &co); + sad_blake3_output_cv(&co, chunk_cv); + total_chunks = h->chunk_state.chunk_counter + 1; + sad_blake3_hasher_add_chunk_cv(h, chunk_cv, total_chunks); + sad_blake3_chunk_init(&h->chunk_state, h->key, total_chunks, h->flags); + } + take = (size_t)SAD_BLAKE3_CHUNK_LEN - sad_blake3_chunk_len(&h->chunk_state); + if (take > len) + take = len; + sad_blake3_chunk_update(&h->chunk_state, data, take); + data += take; + len -= take; + } +} + +static void sad_blake3_hasher_finalize(SadBlake3Hasher *h, unsigned char *out, size_t out_len) +{ + SadBlake3Output output; + int remaining; + sad_blake3_chunk_output(&h->chunk_state, &output); + remaining = h->cv_stack_len; + while (remaining > 0) + { + unsigned int cv[8]; + SadBlake3Output po; + remaining--; + sad_blake3_output_cv(&output, cv); + sad_blake3_parent_output(h->cv_stack[remaining], cv, h->key, h->flags, &po); + output = po; + } + sad_blake3_output_root_bytes(&output, out, out_len); +} + +static void sad_blake3_raw(const char *str, unsigned char digest[32]) +{ + SadBlake3Hasher h; + size_t len = str ? strlen(str) : 0; + sad_blake3_hasher_init(&h); + sad_blake3_hasher_update(&h, (const unsigned char *)(str ? str : ""), len); + sad_blake3_hasher_finalize(&h, digest, 32); +} + +/* بلايك3 / BLAKE3 hash — سلسلة ست عشريّة 64 حرفًا */ +const char *sad_blake3_hash(const char *str) +{ + unsigned char digest[32]; + char *out; + size_t i; + sad_blake3_raw(str, digest); + out = (char *)malloc(65); + if (!out) + return ""; + for (i = 0; i < 32; ++i) + snprintf(out + i * 2, 3, "%02x", digest[i]); + out[64] = '\0'; + return out; +} + +/* هاش_مفتاح / BLAKE3 keyed hash (MAC) — سلسلة ست عشريّة 64 حرفًا. + * المفتاح: 32 بايت يُستعمَل مباشرة؛ غير ذلك يُشتقّ منه مفتاح 32 بايت عبر + * sad_blake3_raw أوّلًا (يطابق منطق المفسّر). */ +const char *sad_blake3_keyed_hash(const char *str, const char *key) +{ + unsigned char key32[32]; + unsigned char digest[32]; + SadBlake3Hasher h; + char *out; + size_t i, klen, slen; + if (!key) + key = ""; + if (!str) + str = ""; + klen = strlen(key); + slen = strlen(str); + if (klen == 32) + memcpy(key32, key, 32); + else + sad_blake3_raw(key, key32); + sad_blake3_hasher_init_keyed(&h, key32); + sad_blake3_hasher_update(&h, (const unsigned char *)str, slen); + sad_blake3_hasher_finalize(&h, digest, 32); + out = (char *)malloc(65); + if (!out) + return ""; + for (i = 0; i < 32; ++i) + snprintf(out + i * 2, 3, "%02x", digest[i]); + out[64] = '\0'; + return out; +} + /* وقت_الآن / Current timestamp */ long long sad_security_timestamp(void) {