// DragonX Wallet - HushChat identity derivation (implementation). #include "chat_identity.h" #include namespace dragonx::chat { // The KDF context is used as the BLAKE2b key, so its length must sit within the primitive's // key bounds. static_assert(kChatIdentityKdfContextLen >= crypto_generichash_KEYBYTES_MIN, "chat identity KDF context is shorter than BLAKE2b's minimum key length"); static_assert(kChatIdentityKdfContextLen <= crypto_generichash_KEYBYTES_MAX, "chat identity KDF context is longer than BLAKE2b's maximum key length"); const char* chatIdentityStatusName(ChatIdentityStatus status) { switch (status) { case ChatIdentityStatus::Ready: return "Ready"; case ChatIdentityStatus::FeatureDisabled: return "FeatureDisabled"; case ChatIdentityStatus::SecretUnavailable: return "SecretUnavailable"; case ChatIdentityStatus::DerivationFailed: return "DerivationFailed"; } return "Unknown"; } std::string chatIdentityPublicKeyHex(const ChatKeyPair& keys) { char hex[crypto_kx_PUBLICKEYBYTES * 2 + 1]; sodium_bin2hex(hex, sizeof hex, keys.public_key.data(), keys.public_key.size()); return std::string(hex); } ChatIdentityResult deriveChatIdentityFromSecret(const std::string& stableSecret, ChatKeyPair& outKeys, bool featureEnabled) { ChatIdentityResult result; auto finish = [&result](ChatIdentityStatus status) -> ChatIdentityResult { result.status = status; result.error_name = chatIdentityStatusName(status); return result; }; if (!featureEnabled) return finish(ChatIdentityStatus::FeatureDisabled); if (stableSecret.empty()) return finish(ChatIdentityStatus::SecretUnavailable); if (sodium_init() < 0) return finish(ChatIdentityStatus::DerivationFailed); unsigned char kxSeed[crypto_kx_SEEDBYTES]; static_assert(sizeof(kxSeed) == kChatKeyBytes, "kx seed size mismatch"); const int hashStatus = crypto_generichash( kxSeed, sizeof kxSeed, reinterpret_cast(stableSecret.data()), stableSecret.size(), reinterpret_cast(kChatIdentityKdfContext), kChatIdentityKdfContextLen); if (hashStatus != 0) { sodium_memzero(kxSeed, sizeof kxSeed); return finish(ChatIdentityStatus::DerivationFailed); } const int keypairStatus = crypto_kx_seed_keypair(outKeys.public_key.data(), outKeys.secret_key.data(), kxSeed); sodium_memzero(kxSeed, sizeof kxSeed); // wipe the seed immediately, success or failure if (keypairStatus != 0) { wipeChatKeyPair(outKeys); return finish(ChatIdentityStatus::DerivationFailed); } result.public_key_hex = chatIdentityPublicKeyHex(outKeys); return finish(ChatIdentityStatus::Ready); } } // namespace dragonx::chat