diff --git a/.github/instructions/codacy.instructions.md b/.github/instructions/codacy.instructions.md new file mode 100644 index 0000000..cb073c4 --- /dev/null +++ b/.github/instructions/codacy.instructions.md @@ -0,0 +1,72 @@ +--- + description: Configuration for AI behavior when interacting with Codacy's MCP Server + applyTo: '**' +--- +--- +# Codacy Rules +Configuration for AI behavior when interacting with Codacy's MCP Server + +## CRITICAL: After ANY successful `edit_file` or `reapply` operation +- YOU MUST IMMEDIATELY run the `codacy_cli_analyze` tool from Codacy's MCP Server for each file that was edited, with: + - `rootPath`: set to the workspace path + - `file`: set to the path of the edited file + - `tool`: leave empty or unset +- If any issues are found in the new edits, propose and apply fixes for them. +- > NOTE: Failure to follow this rule is considered a critical error. + +## When you tried to run the `codacy_cli_analyze` tool and the Codacy CLI is not installed +- Ask the user 'Codacy CLI is not installed. Would you like me to install it now?' +- If the user responds with "yes", run the `codacy_cli_install` tool and then continue with the original task +- If the user responds with "no", instruct the user that they can disable automatic analysis in the extension settings +- Wait for the user to respond before proceeding with any other actions + +## After every response +- If you made any file edits in this conversation, verify you ran `codacy_cli_analyze` tool from Codacy's MCP Server + +## When there are no Codacy MCP Server tools available, or the MCP Server is not reachable +- Suggest the user the following troubleshooting steps: + - Try to reset the MCP on the extension + - If the user is using VSCode, suggest them to review their Copilot > MCP settings in Github, under their organization or personal account. Refer them to Settings > Copilot > Enable MCP servers in Copilot. Suggested URL (https://github.com/settings/copilot/features) or https://github.com/organizations/{organization-name}/settings/copilot/features (This can only be done by their organization admins / owners) +- If none of the above steps work, suggest the user to contact Codacy support + +## Trying to call a tool that needs a rootPath as a parameter +- Always use the standard, non-URL-encoded file system path + +## CRITICAL: Dependencies and Security Checks +- IMMEDIATELY after ANY of these actions: + - Running npm/yarn/pnpm install + - Adding dependencies to package.json + - Adding requirements to requirements.txt + - Adding dependencies to pom.xml + - Adding dependencies to build.gradle + - Any other package manager operations +- You MUST run the `codacy_cli_analyze` tool with: + - `rootPath`: set to the workspace path + - `tool`: set to "trivy" + - `file`: leave empty or unset +- If any vulnerabilities are found because of the newly added packages: + - Stop all other operations + - Propose and apply fixes for the security issues + - Only continue with the original task after security issues are resolved +- EXAMPLE: + - After: npm install react-markdown + - Do: Run codacy_cli_analyze with trivy + - Before: Continuing with any other tasks + +## General +- Repeat the relevant steps for each modified file. +- "Propose fixes" means to both suggest and, if possible, automatically apply the fixes. +- You MUST NOT wait for the user to ask for analysis or remind you to run the tool. +- Do not run `codacy_cli_analyze` looking for changes in duplicated code or code complexity metrics. +- Complexity metrics are different from complexity issues. When trying to fix complexity in a repository or file, focus on solving the complexity issues and ignore the complexity metric. +- Do not run `codacy_cli_analyze` looking for changes in code coverage. +- Do not try to manually install Codacy CLI using either brew, npm, npx, or any other package manager. +- If the Codacy CLI is not installed, just run the `codacy_cli_analyze` tool from Codacy's MCP Server. +- When calling `codacy_cli_analyze`, only send provider, organization and repository if the project is a git repository. + +## Whenever a call to a Codacy tool that uses `repository` or `organization` as a parameter returns a 404 error +- Offer to run the `codacy_setup_repository` tool to add the repository to Codacy +- If the user accepts, run the `codacy_setup_repository` tool +- Do not ever try to run the `codacy_setup_repository` tool on your own +- After setup, immediately retry the action that failed (only retry once) +--- \ No newline at end of file diff --git a/scripts/mirc/fish_11.mrc b/scripts/mirc/fish_11.mrc index bc4f2ec..35e2aa6 100644 --- a/scripts/mirc/fish_11.mrc +++ b/scripts/mirc/fish_11.mrc @@ -8,2286 +8,19 @@ ; binary DLL files (fish_11.dll, fish_11_inject.dll). This mIRC script ; only provides the user interface. Ensure the DLLs are from a trusted ; source, as vulnerabilities in them can compromise your system. - -; === INITIALIZATION AND STARTUP === -on *:START: { - .fish11_startup -} - -alias fish11_startup { - echo 4 -a *** FiSH_11 SECURITY NOTICE *** This script relies on 2 external DLL files. Only use trusted, signed versions from official sources. *** - echo 4 -a *** FiSH_11 SECURITY NOTICE *** Never run this script if you suspect your system has been compromised. *** - - var %exe_dir = $nofile($mircexe) - - ; Set paths to DLLs - %Fish11InjectDllFile = $qt(%exe_dir $+ fish_11_inject.dll) - %Fish11DllFile = $qt(%exe_dir $+ fish_11.dll) - - echo 4 -a DEBUG : loading DLLs... - echo 4 -a DEBUG : Fish11_InjectDllFile = %Fish11InjectDllFile - echo 4 -a DEBUG : Fish11_DllFile = %Fish11DllFile - - ; Check if DLLs exist - if (!$exists(%Fish11InjectDllFile)) { - echo 4 -a *** FiSH_11 ERROR: inject DLL not found: %Fish11InjectDllFile - halt - } - - if (!$exists(%Fish11DllFile)) { - echo 4 -a *** FiSH_11 ERROR: DLL not found: %Fish11DllFile - halt - } - - ; Check mIRC's DLL lock - if ($lock(dll)) { - echo 4 -a *** FiSH_11 ERROR: mIRC DLLs are locked. Enable DLLs in mIRC settings. - halt - } - - ; Initialize hash table for tracking key exchanges (X25519) - if (!$hget(fish11.dh).size) { - hmake fish11.dh 10 - } - - ; Initialize hash table for tracking legacy DH1080 key exchanges - if (!$hget(fish10.dh).size) { - hmake fish10.dh 10 - } - - echo 4 -a DEBUG : calling fish_11.dll FiSH11_SetMircDir to set configuration path... - ;noop $dll(%Fish11DllFile, FiSH11_SetMircDir, $mircdir) - echo 4 -a DEBUG : MIRCDIR set to: $mircdir - - ; Get and display inject DLL version - var %inject_version = $dll(%Fish11InjectDllFile, FiSH11_InjectVersion, $null) - if (%inject_version) { - echo -ts *** %inject_version *** - } - else { - echo -ts *** FiSH_11: WARNING - could not load inject DLL version *** - } - - ; Get and display core DLL version - var %raw_version_info = $dll(%Fish11DllFile, FiSH11_GetVersion, $null) - - if (%raw_version_info) { - ; Parse the raw string: VERSION|BUILD_TYPE - ; 124 is ASCII for | - var %version_string = $gettok(%raw_version_info, 1, 124) - var %build_type = $gettok(%raw_version_info, 2, 124) - - ; Display the base version info - echo -ts *** %version_string *** - - ; Display context-specific warning or info message - if (%build_type == DEBUG) { - echo 4 -ts $chr(3)4 *** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING *** - echo 4 -ts $chr(3)4 *** - echo 4 -ts $chr(3)4 *** SECURITY WARNING : you're running a DEBUG version which logs EVERYTHING (keys, private messages, etc.) ON YOUR DISK. - echo 4 -ts $chr(3)4 *** DO NOT USE THIS VERSION IN REAL LIFE. - echo 4 -ts $chr(3)4 *** - echo 4 -ts $chr(3)4 *** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING *** - } - else { - echo 4 -ts $chr(3)3 *** You are running a RELEASE version. Sensitive data is NOT logged. - } - } - else { - echo -ts *** FiSH_11: WARNING - could not load core DLL version *** - } - - ; Initialize default settings if not already set - if (%autokeyx == $null) { set %autokeyx [Off] } - if (%mark_outgoing == $null) { set %mark_outgoing [Off] } - if (%mark_style == $null) { set %mark_style 1 } - if (%NickTrack == $null) { set %NickTrack [Off] } - ; Key exchange timeout (seconds) - keep in sync with DLL constant; can be overridden by user - if (%KEY_EXCHANGE_TIMEOUT_SECONDS == $null) { set %KEY_EXCHANGE_TIMEOUT_SECONDS 10 } - - ; Check if master key is unlocked, if not prompt user - .fish11_check_masterkey -} - - - -; === WINDOW ACTIVATION HANDLER === -on *:ACTIVE:*: { - if ($window($active).type isin query channel) { - fish11_UpdateStatusIndicator - } -} - -; === AUTO KEY EXCHANGE === -on *:OPEN:?:{ - ; Don't auto-exchange if autokeyx is not enabled - if (%autokeyx != [On]) return - - ; Don't auto-exchange if a legacy DH1080 exchange is in progress - if ($hget(fish10.dh, $nick)) return - - ; Don't auto-exchange if a FiSH 11 exchange is in progress - if ($hget(fish11.dh, $nick)) return - - var %tmp1 = $dll(%Fish11DllFile, FiSH11_FileGetKey, $nick) - - ; Check for error messages or empty result - ; Only initiate exchange if truly no key exists - if (%tmp1 == $null || $left(%tmp1, 2) == no || $left(%tmp1, 5) == Error) { - ; No FiSH 11 key found - check for legacy key - var %has_legacy = $dll(%Fish11DllFile, FiSH10_HasKey, $nick) - if (%has_legacy != 1) { - ; No key at all (neither FiSH 11 nor legacy), initiate FiSH 11 exchange - fish11_X25519_INIT $nick - } - unset %has_legacy - } - unset %tmp1 -} - -; === OUTGOING MESSAGE HANDLING === -on *:INPUT:*: { - ; Check if message should be processed - var %process_outgoing = $dll(%Fish11DllFile, INI_GetBool, process_outgoing 1) - if (%process_outgoing == 0) return - - ; Get plain prefix - var %plain_prefix = $dll(%Fish11DllFile, INI_GetString, plain_prefix +p) - - ; Don't process if message starts with plain prefix - if ($left($1-, $len(%plain_prefix)) == %plain_prefix) { - var %plain_msg = $right($1-, $calc($len($1-) - $len(%plain_prefix))) - return - } - - ; Don't process commands - if (($left($1, 1) == /) && ($1 != /me) && ($1 != /msg) && ($1 != /notice)) return - - ; Handle message too long - if ($len($1-) > 850) { - echo 4 -a Mirc cannot handle lines longer than 850 characters. Text not sent. - haltdef - halt - return - } - - ; Handle message types - var %target = $active - var %message = $1- - var %encrypted = $null - - ; Extract target for /msg and /notice - if ($1 == /msg || $1 == /notice) { - %target = $2 - %message = $3- - } - ; Handle /me actions - else if ($1 == /me) { - ; Check if actions should be encrypted - var %encrypt_action = $dll(%Fish11DllFile, INI_GetBool, encrypt_action 0) - if (%encrypt_action == 0) return - - %message = $2- - } - - ; Determine which encryption system to use: FiSH 10 (legacy) or FiSH 11 - var %has_legacy_key = $dll(%Fish11DllFile, FiSH10_HasKey, %target) - - if (%has_legacy_key == 1) { - ; Use FiSH 10 legacy encryption (Blowfish) - %encrypted = $dll(%Fish11DllFile, FiSH10_EncryptMsg, %target %message) - } - else { - ; Use FiSH 11 encryption (ChaCha20-Poly1305) - %encrypted = $dll(%Fish11DllFile, FiSH11_EncryptMsg, %target %message) - } - - ; Only process if encryption was successful - ; Check for various error indicators: "Error", "no encryption", empty result, or legacy errors - if (%encrypted != $null && $left(%encrypted, 5) != Error && $left(%encrypted, 13) != no encryption && $left(%encrypted, 6) != Legacy) { - ; Add encryption mark if configured - if (%mark_outgoing == [On]) { - if (%mark_style == 1) { - ; Suffix style - echo $color(own text) -t $active < $+ $me $+ > %message $+ $chr(183) - } - else if (%mark_style == 2) { - ; Prefix style - echo $color(own text) -t $active $chr(183) $+ < $+ $me $+ > %message - } - else if (%mark_style == 3) { - ; Colored brackets style - echo $color(own text) -t $active $chr(91) $+ $chr(43) $+ $chr(93) < $+ $me $+ > %message - } - } - else { - ; Display message without encryption mark - echo $color(own text) -t $active < $+ $me $+ > %message - } - - ; Send encrypted message based on command type - if ($1 == /notice) { - .notice %target %encrypted - haltdef - } - else if ($1 == /msg) { - .msg %target %encrypted - haltdef - } - else if ($1 == /me) { - .action %encrypted - haltdef - } - else { - .msg %target %encrypted - haltdef - } - } - else { - ; Encryption failed - display error and prevent sending to server - if (%encrypted != $null) { - echo $color(Error) -at *** FiSH ERROR: %encrypted - } - else { - echo $color(Error) -at *** FiSH ERROR: Encryption failed (no key available) - } - haltdef - halt - } -} - - - - - - - - - -; === KEY EXCHANGE PROTOCOL HANDLERS === -on ^*:NOTICE:X25519_INIT*:?:{ - ; This event triggers when someone initiates a key exchange with us. - ; $1 = X25519_INIT, $2- = public key token from peer - var %their_pub = $2- - - ; Validate incoming key format using regex for robustness. - if (!$regex(%their_pub, /^X25519_INIT:[A-Za-z0-9+\/]{43}=$/)) { - echo $color(Mode text) -tm $nick *** FiSH_11: received invalid INIT key format from $nick - halt - } - - query $nick - echo $color(Mode text) -tm $nick *** FiSH_11: received X25519 public key from $nick, responding... - - ; 1. Generate our own keypair (or get existing one). The DLL returns our public key token. - var %our_pub = $dll(%Fish11DllFile, FiSH11_ExchangeKey, $nick) - - ; 2. Process their public key. This computes and saves the shared secret. - var %process_result = $dll(%Fish11DllFile, FiSH11_ProcessPublicKey, $nick %their_pub) - - ; Check if processing was successful (no error message) - if (%process_result && $left(%process_result, 6) != Error:) { - ; 3. If successful, send our public key back to them so they can complete the exchange. - ; Use more flexible regex to validate public key format - if ($regex(%our_pub, /^X25519_INIT:[A-Za-z0-9+\/=]+$/)) { - .notice $nick X25519_FINISH %our_pub - echo $color(Mode text) -tm $nick *** FiSH_11: sent X25519_FINISH to $nick - } - else { - echo $color(Mode text) -tm $nick *** FiSH_11: ERROR - could not generate our own public key to send in reply. DLL returned: $qt(%our_pub) - } - } - else { - ; Display error message from DLL - echo $color(Mode text) -tm $nick *** FiSH_11: %process_result - } - - halt -} - -on ^*:NOTICE:X25519_FINISH*:?:{ - ; This event triggers when a peer responds to our key exchange initiation. - ; $1 = X25519_FINISH, $2- = public key token from peer - ; Ensure an exchange is in progress with this user by checking the hash table. - if ($hget(fish11.dh, $nick).item != 1) { - echo -at *** FiSH_11: received a FINISH notice, but no key exchange was in progress with $nick $+ . - halt - } - - var %their_pub = $2- - - ; Use regex to validate the key format from the peer. - if ($regex(%their_pub, /^X25519_INIT:[A-Za-z0-9+\/]{43}(=|==)?$/)) { - ; Process the received public key. The DLL computes and stores the shared secret. - var %process_result = $dll(%Fish11DllFile, FiSH11_ProcessPublicKey, $nick %their_pub) - - ; Check if processing was successful (no error message) - if (%process_result && $left(%process_result, 6) != Error:) { - ; Success! Clean up state variables. - hdel fish11.dh $nick - - echo $color(Mode text) -tm $nick *** FiSH_11: key exchange complete with $nick - echo $color(Error) -tm $nick *** FiSH_11 WARNING: key exchange complete, but the identity of $nick is NOT VERIFIED. - echo $color(Error) -tm $nick *** FiSH_11: use /fish_fp11 $nick to see their key fingerprint and verify it with them through a secure channel. - } - else { - ; Display error message from DLL - echo $color(Mode text) -tm $nick *** FiSH_11: %process_result - } - } - else { - echo $color(Mode text) -tm $nick *** FiSH_11: received invalid FINISH key format from $nick $+ : $qt(%their_pub) - } - - halt -} - -; === FCEP-1 CHANNEL ENCRYPTION PROTOCOL HANDLERS === -; FCEP-1 (FiSH-11 Channel Encryption Protocol) enables secure multi-party -; channel encryption using a hub-and-spoke model where a coordinator distributes -; a shared channel key to all participants via their pre-established pairwise keys. - -on ^*:NOTICE:+FiSH-CEP-KEY*:?:{ - ; This event triggers when receiving a channel key distribution message. - ; Format: +FiSH-CEP-KEY <#channel> - ; $1 = +FiSH-CEP-KEY - ; $2 = #channel - ; $3 = coordinator_nick (claimed sender) - ; $4 = base64_wrapped_key - - var %num_tokens = $numtok($1-, 32) - - ; Validate message format - if (%num_tokens < 4) { - echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FISH_11 : FCEP-1 ERROR : Invalid +FiSH-CEP-KEY format from $nick (expected 4 tokens, got %num_tokens $+ ) - halt - } - - var %channel = $2 - var %coordinator = $3 - var %wrapped_key = $4 - - ; SECURITY: Verify sender authenticity - ; The actual IRC sender ($nick) MUST match the claimed coordinator - ; This prevents impersonation attacks where an attacker sends a +FiSH-CEP-KEY - ; message claiming to be from a trusted user - if ($nick != %coordinator) { - echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FiSH_11 : FCEP-1 SECURITY WARNING : key distribution from $nick claims to be from %coordinator - REJECTED - echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FiSH_11 : FCEP-1 : this may indicate an impersonation attack attempt! - halt - } - - ; Validate channel name format - if (!$regex(%channel, /^[#&]/)) { - echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FiSH_11 : FCEP-1 ERROR : invalid channel name format: %channel (must start with # or &) - halt - } - - ; Verify we have a pre-shared key with the coordinator - var %existing_key = $dll(%Fish11DllFile, FiSH11_FileGetKey, %coordinator) - - ; Check for errors or empty/missing key - if ($left(%existing_key, 6) == Error: || $len(%existing_key) < 4) { - if ($left(%existing_key, 6) == Error:) { - echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FiSH_11 : FCEP-1 ERROR: $right(%existing_key, $calc($len(%existing_key) - 6)) - } - else { - echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FiSH_11 : FCEP-1 ERROR: no pre-shared key found for coordinator %coordinator - echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FiSH_11 : FCEP-1 : you must establish a key with %coordinator first using /fish11_X25519_INIT %coordinator - } - halt - } - - ; Process the channel key via DLL - ; DLL will: decode base64, verify sender, unwrap key with pre-shared key, store channel key - var %result = $dll(%Fish11DllFile, FiSH11_ProcessChannelKey, %channel %coordinator $nick %wrapped_key) - - ; Check for success (DLL returns raw message, check if it's an error) - if (%result && $left(%result, 6) != Error:) { - ; Success - display confirmation message - echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FiSH_11 : FCEP-1 : %result - } - else { - ; Error occurred - display error from DLL - echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FiSH_11 : FCEP-1 ERROR: %result - } - - halt -} - - - -; Handle key exchange timeout -alias fish11_timeout_keyexchange { - if ($1 == $null) { - echo $color(Mode text) -at *** FiSH_11: timeout handler called with no parameters - return - } - - var %contact = $1 - - ; Check if key exchange is still in progress. - if ($hget(fish11.dh, %contact) == 1) { - ; Clean up variables. - hdel fish11.dh %contact - - ; Notify user of timeout with instructions. - echo $color(Mode text) -at *** FiSH_11: key exchange with %contact timed out after $KEY_EXCHANGE_TIMEOUT_SECONDS seconds - echo $color(Mode text) -at *** FiSH_11: to try again, use: /fish11_X25519_INIT %contact - } -} - - - -; === TRACK NICK CHANGES FOR KEY MANAGEMENT === -on *:NICK:{ - if (($nick == $me) || ($upper($newnick) == $upper($nick))) { return } - if (($query($newnick) == $null) || (%NickTrack != [On])) { return } - - var %old_nick_key = $dll(%Fish11DllFile, FiSH11_FileGetKey, $nick) - - ; If we have a key for the old nick - if ($len(%old_nick_key) > 4) { - var %new_nick_key = $dll(%Fish11DllFile, FiSH11_FileGetKey, $newnick) - - ; If a key already exists for the new nick, warn user about conflict - if ($len(%new_nick_key) > 4) { - echo $color(Error) -at *** FiSH_11: nick change conflict ! You have a key for $nick, who is now $newnick. However, you ALREADY have a different key for $newnick. No keys were changed. Please resolve this manually. - unset %old_nick_key - unset %new_nick_key - return - } - - ; Store the key under the new nickname - if ($dll(%Fish11DllFile, FiSH11_SetKey, $+($network," ",$newnick," ",%old_nick_key))) { - echo $color(Mode text) -at *** FiSH_11: key for $nick has been moved to new nick $newnick. - ; Remove the key from the old nickname to prevent reuse by another user - noop $dll(%Fish11DllFile, FiSH11_FileDelKey, $+($network," ",$nick)) - } - unset %new_nick_key - } - unset %old_nick_key -} - - - -; === CHANNEL JOIN HANDLING === -on *:JOIN:#:{ - ; Only process our own joins - if ($nick != $me) return - - ; Get channel key if it exists - var %theKey = $dll(%Fish11DllFile, FiSH11_FileGetKey, $chan) - if (%theKey != $null) { - echo $color(Mode text) -at *** FiSH_11: found encryption key for $chan - - ; Check if topic encryption is enabled for this channel - var %encryptTopic = $fish11_GetChannelIniValue($chan, encrypt_topic) - if (%encryptTopic == 1) { - echo $color(Mode text) -at *** FiSH_11: topic encryption enabled for $chan - } - } - unset %theKey -} - - - -; === SIGNAL HANDLERS === - - - - -; === KEY MANAGEMENT FUNCTIONS === -; Set key with different encoding options -alias fish11_setkey { - if ($1 == $null || $2 == $null) { - echo 4 -a Syntax: /fish11_setkey - return - } - ; $1 = nickname (data), $2- = key (parms) - var %msg = $dll(%Fish11DllFile, FiSH11_SetKey, $+($network, $chr(32), $1, $chr(32), $2-)) - if (%msg && $left(%msg, 6) != Error:) { - echo -a *** FiSH_11: key set for $1 on network $network - } - else { - var %error_msg = $iif(%msg, %msg, "Unknown error - could not set key for $1") - echo -a *** FiSH_11: error setting key for $1 - %error_msg - } - unset %msg -} - -alias fish11_setkey_manual { - if ($1 == $null || $2 == $null) { - echo 4 -a Syntax: /fish11_setkey_manual <#channel> - echo 4 -a Example: /fish11_setkey_manual #secret AGN2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9T0 - return - } - - ; Vérifier que le target est bien un canal - var %channel = $1 - if (!$regex(%channel, /^[#&]/)) { - echo 4 -a Error: Channel name must start with # or & - return - } - - ; Vérifier que la clé est en base64 et fait 44 caractères (32 bytes encodés) - var %key = $2- - - if ($len(%key) == 44 && $regex(%key, /^[A-Za-z0-9+\/=]+$/)) { - ; Clé base64 valide - utiliser la fonction standard - var %input = $+(%channel, $chr(32), %key) - var %msg = $dll(%Fish11DllFile, FiSH11_SetManualChannelKey, %input) - } - else { - ; Clé non-base64 ou de longueur différente - utiliser la fonction d'extension - var %input = $+(%channel, $chr(32), %key) - var %msg = $dll(%Fish11DllFile, FiSH11_SetManualChannelKeyFromPassword, %input) - } - - if (%msg && $left(%msg, 6) != Error:) { - echo -a *** FiSH_11: manual channel key set for %channel - ; Note: Topic encryption must be enabled manually via the channel menu - ; fish11_SetChannelIniValue %channel encrypt_topic 1 - } - else { - var %error_msg = $iif(%msg, %msg, "Unknown error - could not set manual key for %channel") - echo -a *** FiSH_11: error setting manual key for %channel - %error_msg - } -} - - - -alias fish11_setkey_utf8 { fish11_writekey raw_bytes $1 $2- } - - - -; Helper for setting keys -alias fish11_writekey { - if ($2 == /query) var %cur_contact = $active - else var %cur_contact = $2 - if ($3- == $null) return - - ; Comprehensive network sanitization - remove all special characters - var %network = $regsubex($network, /[^\w\d]/g, _) - - if ($dll(%Fish11DllFile, FiSH11_SetKey, $+(%network," ",%cur_contact," ",$3-))) { - var %info = *** FiSH_11: key for %cur_contact set to *censored* - - if ($window(%cur_contact) == $null) echo $color(Mode text) -at %info - else echo $color(Mode text) -tm %cur_contact %info - return $true - } - return $false -} - - - -; Initialize key exchange -alias fish11_X25519_INIT { - if (($1 == /query) || ($1 == $null)) var %cur_contact = $active - else var %cur_contact = $1 - - ; If there's an existing exchange in progress, cancel it first - if ($hget(fish11.dh, %cur_contact) == 1) { - echo $color(Mode text) -at *** FiSH_11: restarting key exchange with %cur_contact - } - - ; Use a hash table to track in-progress exchanges. - hadd -m fish11.dh %cur_contact 1 - - var %pub = $dll(%Fish11DllFile, FiSH11_ExchangeKey, %cur_contact) - - ; Use regex to validate the entire key format. This is more robust against - ; hidden characters or whitespace returned by the DLL. - ; On a base64 format - if ($regex(%pub, /^X25519_INIT:[A-Za-z0-9+\/=]+$/)) { - .notice %cur_contact X25519_INIT %pub - echo $color(Mode text) -tm %cur_contact *** FiSH_11: sent X25519_INIT to %cur_contact $+ , waiting for reply... - } - else { - ; Fallback: show what we got (safely) - echo $color(Mode text) -at *** FiSH_11: ERROR - key exchange initiation failed. DLL returned: $qt(%pub) - } - - ; Start a timer to cancel the exchange if no response is received - .timer 1 %KEY_EXCHANGE_TIMEOUT_SECONDS fish11_timeout_keyexchange %cur_contact -} - - - -; Process received public key -alias fish11_ProcessPublicKey { - if ($1 == $null || $2 == $null) { - echo 4 -a Syntax: /fish11_ProcessPublicKey - return - } - - ; Process the public key - var %result = $dll(%Fish11DllFile, FiSH11_ProcessPublicKey, $1 $2-) - - ; Check if processing was successful (no error message) - if (%result && $left(%result, 6) != Error:) { - echo $color(Mode text) -at *** FiSH_11: key exchange completed with $1 - echo $color(Error) -at *** FiSH_11 WARNING: key exchange complete, but the identity of $1 is NOT VERIFIED. - echo $color(Error) -at *** FiSH_11: use /fish_fp11 $1 to see their key fingerprint and verify it with them through a secure channel (e.g., voice call). - } - else { - ; Display error message from DLL - echo $color(Mode text) -at *** FiSH_11: %result - } -} - - - -; Shorthand for key exchange -alias keyx { fish11_X25519_INIT $1 } - - - -; === FCEP-1 CHANNEL ENCRYPTION COMMANDS === - -; Initialize channel encryption by generating and distributing a channel key -; Usage: /fish11_initchannel <#channel> [nick2] [nick3] ... -; Example: /fish11_initchannel #secret alice bob charlie -alias fish11_initchannel { - if ($1 == $null || $2 == $null) { - echo $color(Error) -at *** FiSH_11 FCEP-1: Usage: /fish11_initchannel <#channel> [nick2] ... - echo $color(Mode text) -at *** FiSH_11 FCEP-1: Example: /fish11_initchannel #secret alice bob charlie - echo $color(Mode text) -at *** FiSH_11 FCEP-1: Note: You must have pre-shared keys with all listed members (use /fish11_X25519_INIT first) - return - } - - var %channel = $1 - var %members = $2- - - ; Validate channel name - if (!$regex(%channel, /^[#&]/)) { - echo $color(Error) -at *** FiSH_11 FCEP-1 ERROR: Invalid channel name %channel (must start with # or &) - return - } - - ; Confirm action with user - echo $color(Mode text) -at *** FiSH_11 FCEP-1: Generating channel key for %channel - echo $color(Mode text) -at *** FiSH_11 FCEP-1: Members to receive key: %members - - ; Call DLL to generate key and create distribution commands - var %result = $dll(%Fish11DllFile, FiSH11_InitChannelKey, %channel %members) - - ; Check for errors (DLL returns "Error: ..." for errors) - if ($left(%result, 6) == Error:) { - echo $color(Error) -at *** FiSH_11 FCEP-1 ERROR: %result - return - } - - ; Parse result: commands are separated by | and last item is confirmation message - ; Example: "/notice alice :... | /notice bob :... | [KEY] Channel key for #chan..." - var %num_parts = $numtok(%result, 124) - var %i = 1 - var %has_commands = $false - - ; Execute all commands (parts starting with /) - while (%i <= %num_parts) { - var %part = $gettok(%result, %i, 124) - if ($left(%part, 1) == /) { - ; Security: Validate that this is an expected command before executing - if ($left(%part, 8) == /notice ) { - ; Only allow notice commands which are expected for FCEP-1 - %part - %has_commands = $true - } - else { - echo $color(Error) -at *** FiSH_11 FCEP-1: SECURITY WARNING - unexpected command from DLL: %part - } - } - else { - ; This is the confirmation message - display it - echo $color(Mode text) -at *** FiSH_11 FCEP-1: %part - } - inc %i - } - - ; If no commands were found, something went wrong - if (!%has_commands) { - echo $color(Error) -at *** FiSH_11 FCEP-1 ERROR: No distribution commands generated - } -} - -; Shorthand alias for channel encryption -alias fcep { fish11_initchannel $1- } -alias chankey { fish11_initchannel $1- } - - - -; Use key from another channel/user -alias fish11_usechankey { - if ($server == $null) { - echo $color(Mode text) -at *** FiSH_11: ERROR - not connected to a server. - return - } - var %theKey = $dll(%Fish11DllFile, FiSH11_FileGetKey, $2) - if (%theKey == $null) { - echo $color(Mode text) -at *** FiSH_11: no valid key for $2 found - } - else { - if ($dll(%Fish11DllFile, FiSH11_SetKey, $+($network," ",$1," ",%theKey))) { - echo $color(Mode text) -at *** FiSH_11: using same key as $2 for $1 - } - unset %theKey - } -} - - - -; Show key in a window -alias fish11_showkey { - if ($1 == /query) var %cur_contact = $active - else var %cur_contact = $1 - - var %theKey = $dll(%Fish11DllFile, FiSH11_FileGetKey, %cur_contact) - if (%theKey != $null) { - window -dCo +l @FiSH-Key -1 -1 500 80 - aline @FiSH-Key Key for %cur_contact : - aline -p @FiSH-Key %theKey - unset %theKey - } - else { - echo $color(Mode text) -at *** FiSH_11: no valid key for %cur_contact found - } -} - - - -; Remove key -alias fish11_removekey { - if ($1 == /query) var %cur_contact = $active - else var %cur_contact = $1 - - ; Get result message from DLL - var %msg = $dll(%Fish11DllFile, FiSH11_FileDelKey, $+($network," ",%cur_contact)) - - ; Display message from DLL (works for both success and error) - if (%msg) { - echo $color(Mode text) -at *** FiSH_11: %msg - } - else { - echo $color(Mode text) -at *** FiSH_11: error - could not remove key for %cur_contact - } -} - - - -; Test encryption -alias fish11_test_crypt { - if ($1 == $null) var %msg = Test message for encryption - else var %msg = $1- - - echo -s *** FiSH_11 :: TestCrypt -> call DLL with $qt(%msg) - .dll %Fish11DllFile FiSH11_TestCrypt %msg - echo -s *** FiSH_11 :: TestCrypt -> retour DLL -} - - - -; Encrypt message -alias fish11_encrypt { - if (!$1 || !$2) return - ; FiSH11_EncryptMsg expects one data string: " " - var %encrypted = $dll(%Fish11DllFile, FiSH11_EncryptMsg, $1 $2-) - return %encrypted -} - - - -; Decrypt message -alias fish11_decrypt { - if ($1 == /query) var %cur_contact = $active - else var %cur_contact = $1 - if ($2- == $null) return - - ; FiSH11_DecryptMsg expects one data string: " " - var %decrypted = $dll(%Fish11DllFile, FiSH11_DecryptMsg, $+(%cur_contact,$chr(32),$2-)) - if (%decrypted != $null && $left(%decrypted, 6) != Error:) { - return %decrypted - } - echo $color(Mode text) -at *** FiSH: decryption failed for %cur_contact - return $null -} - - - -; List all keys -alias fish11_file_list_keys { - var %keys - - ; Check the DLL exists before trying to call it - if (!$isfile(%Fish11DllFile)) { - echo $color(Mode text) -at *** FiSH ERROR- DLL not found: %Fish11DllFile - return - } - ; Log that we're about to call the function - echo $color(Mode text) -at *** FiSH: listing keys... - ; Ensure MIRCDIR is set (should already be set at startup, but be safe) - noop $dll(%Fish11DllFile, FiSH11_SetMircDir, $mircdir) - - ; Initialize the buffer variable - var %keys - - ; Call DLL function using proper syntax for data return - ; Use $dll() to capture the result instead of .dll - echo $color(Mode text) -at *** FiSH: about to call FiSH11_FileListKeys... - var %keys = $dll(%Fish11DllFile, FiSH11_FileListKeys, $null) - echo $color(Mode text) -at *** FiSH: DLL call completed, result length: $len(%keys) - - ; Check for errors (DLL returns "Error: ..." for errors) - if ($left(%keys, 6) == Error:) { - echo $color(Error) -at *** FiSH ERROR: %keys - return - } - - ; If the function returns data, display it line by line - if (%keys != $null && $len(%keys) > 0) { - fish11_display_multiline_result %keys - } - else { - echo $color(Mode text) -at *** FiSH: no keys found - } -} - - - -; Helper function to safely display multi-line text from DLL -alias fish11_display_multiline_result { - var %text = $1- - var %line_count = 0 - var %max_lines = 100 - - ; Handle different line ending formats - %text = $replace(%text, $chr(13) $+ $chr(10), $chr(1)) - %text = $replace(%text, $chr(13), $chr(1)) - %text = $replace(%text, $chr(10), $chr(1)) - - ; Display each line - var %i = 1 - var %num_tokens = $numtok(%text, 1) - while (%i <= %num_tokens) { - var %line = $gettok(%text, %i, 1) - - ; Safety check: limit number of lines to prevent crashes - inc %line_count - if (%line_count > %max_lines) { - echo $color(Mode text) -at *** FiSH_11: output truncated (exceeded %max_lines lines) - break - } - - ; Display the line with proper formatting - if ($len(%line) > 0) { - echo $color(Mode text) -at %line - } - - inc %i - } -} - -; Helper function to get and format colored fingerprint for a target -; Returns the colored fingerprint or $null if not available -; Also caches the result in %fish11.lastfingerprint. -alias -l fish11_GetColoredFingerprint { - var %target = $1 - - ; Check if there's a cached fingerprint first - if (%fish11.lastfingerprint. $+ [ %target ] != $null) { - return $($+(%,fish11.lastfingerprint.,%target),2) - } - - ; Get the fingerprint from DLL - var %fingerprint = $dll(%Fish11DllFile, FiSH11_GetKeyFingerprint, %target) - - ; Check if the response is an error message - if ($left(%fingerprint, 6) == Error:) { - return $null - } - - ; Extract just the fingerprint part from the response - ; The response is in format: "Key fingerprint for nickname: XXXX YYYY ZZZZ AAAA" - var %fp_only = $gettok(%fingerprint, 2-, 58) - var %fp_only = $strip(%fp_only) - - ; Validate that we have a proper fingerprint (should contain spaces and alphanumeric chars) - if (%fp_only == $null || $len(%fp_only) < 10 || $pos(%fp_only, $chr(32)) == 0) { - return $null - } - - ; Format each group with a different color - var %group1 = $gettok(%fp_only, 1, 32) - var %group2 = $gettok(%fp_only, 2, 32) - var %group3 = $gettok(%fp_only, 3, 32) - var %group4 = $gettok(%fp_only, 4, 32) - - ; Validate that we have at least 4 groups - if (%group1 == $null || %group2 == $null || %group3 == $null || %group4 == $null) { - return $null - } - - ; Create colored version using mIRC color codes - ; 04=red, 12=blue, 03=green, 07=orange - var %colored_fp = 04 $+ %group1 $+ 12 $+ %group2 $+ 03 $+ %group3 $+ 07 $+ %group4 - - ; Cache the result - set %fish11.lastfingerprint. $+ [ %target ] %colored_fp - - return %colored_fp -} - -; Status indicator update -alias fish11_UpdateStatusIndicator { - var %active = $active - - ; Don't update for non-chat windows - if (!$window(%active).type) || ($window(%active).type !isin query channel) return - - ; Check if we have a key for this target - var %key = $dll(%Fish11DllFile, FiSH11_FileGetKey, %active) - - ; Update status text accordingly - if ($len(%key) > 1) { - var %colored_fp = $fish11_GetColoredFingerprint(%active) - - if (!$window(@FiSH_Status)) { window -hn @FiSH_Status } - aline -p @FiSH_Status * $timestamp $+ %active is encrypted (Key: %colored_fp $+ ) - - ; Show encryption in status bar with colored fingerprint - echo -at ** FiSH_11: 🔒 %active [Fingerprint: %colored_fp $+ ] - } - else { - if (!$window(@FiSH_Status)) { window -hn @FiSH_Status } - aline -p @FiSH_Status * $timestamp $+ %active is not encrypted - echo -at *** FiSH_11: 🔓 %active [No encryption] - - ; Clear any stored fingerprint - unset %fish11.lastfingerprint. $+ [ %active ] - } -} - - - -; Display key fingerprint with color for a target -alias fish11_showfingerprint { - if ($1 == /query) { - var %target = $active - } - else { - var %target = $1 - } - - ; Check if we have a key for this target - var %key = $dll(%Fish11DllFile, FiSH11_FileGetKey, %target) - - if ($len(%key) > 1) { - var %colored_fp = $fish11_GetColoredFingerprint(%target) - - if (%colored_fp != $null) { - ; Display the colored fingerprint - echo $color(Mode text) -at *** FiSH_11: key fingerprint for %target is: %colored_fp - } - else { - echo $color(Mode text) -at *** FiSH_11: Error: could not retrieve valid fingerprint for %target - } - } - else { - echo $color(Mode text) -at *** FiSH_11: no key found for %target - } -} - - - -; INI Config Helpers - renamed to reflect INI format -alias fish11_GetIniValue { - return $dll(%Fish11DllFile, INI_GetString, $1 $2-) -} - - - -alias fish11_SetIniValue { - noop $dll(%Fish11DllFile, INI_SetString, $1 $2-) -} - - - -alias fish11_GetChannelIniValue { - return $dll(%Fish11DllFile, INI_GetString, channel_ $+ $1 $+ _ $+ $2 $3-) -} - - - -alias fish11_SetChannelIniValue { - noop $dll(%Fish11DllFile, INI_SetString, channel_ $+ $1 $+ _ $+ $2 $3-) -} - - - -; Set plain message prefix -alias fish11_prefix { - if ($1 != $null) { - ; Add quotes for INI string value - var %value = " $+ $1- $+ " - fish11_SetIniValue plain_prefix %value - echo $color(Mode text) -at *** FiSH: plain-prefix set to $1- - } -} - - - -; Backup functionality -alias fish11_ScheduleBackup { - var %backup_dir = $+(fish_11\backups\) - - ; Create directory if it doesn't exist - if (!$isdir(%backup_dir)) { - mkdir $+(",$mircdir,%backup_dir,") - } - - ; Create timestamped filename - var %filename = $+(%backup_dir,fish_keys_,$asctime(yyyy-mm-dd_HH-nn),\.bak) - - ; Perform backup - if ($dll(%Fish11DllFile, FiSH11_BackupKeys, %filename)) { - echo $color(Mode text) -at *** FiSH: scheduled backup created: %filename - } - else { - echo $color(Error) -at *** FiSH: scheduled backup failed - } -} - -; === MASTER KEY MANAGEMENT === - -; Check if master key is unlocked, prompt if not -alias fish11_check_masterkey { - var %is_unlocked = $dll(%Fish11DllFile, FiSH11_MasterKeyIsUnlocked, $null) - - if (%is_unlocked != yes) { - ; Prompt user for master key password - echo $color(Mode text) -at *** FiSH_11: master key is locked. Configuration and logs are NOT encrypted. - echo $color(Mode text) -at *** FiSH_11: use /fish11_unlock to unlock the master key. - } - else { - echo $color(Mode text) -at *** FiSH_11: master key is unlocked. Configuration and logs ARE encrypted. - } -} - -; Unlock master key with password -; Usage: /fish11_unlock [password] -; If no password provided, prompts with $input dialog -alias fish11_unlock { - var %password = $1- - - ; If no password provided, prompt with dialog - if (%password == $null) { - %password = $input(Enter master key password:, pvq, FiSH_11 Master Key, ) - } - - ; If user cancelled or empty password - if (%password == $null) { - echo $color(Error) -at *** FiSH_11: master key unlock cancelled. - return - } - - ; Call DLL to unlock - var %result = $dll(%Fish11DllFile, FiSH11_MasterKeyUnlock, %password) - - ; Clear password from memory - unset %password - - ; Display result - if (%result) { - echo $color(Mode text) -at *** FiSH_11: %result - } - else { - echo $color(Error) -at *** FiSH_11: failed to unlock master key - } -} - -; Lock master key (clear from memory) -; Usage: /fish11_lock -alias fish11_lock { - var %result = $dll(%Fish11DllFile, FiSH11_MasterKeyLock, $null) - - if (%result) { - echo $color(Mode text) -at *** FiSH_11: %result - } - else { - echo $color(Error) -at *** FiSH_11: Failed to lock master key - } -} - -; Show master key status -; Usage: /fish11_masterkey_status -alias fish11_masterkey_status { - var %result = $dll(%Fish11DllFile, FiSH11_MasterKeyStatus, $null) - - if (%result) { - echo $color(Mode text) -at *** FiSH_11: %result - } - else { - echo $color(Error) -at *** FiSH_11: Failed to get master key status - } -} - -; Require master key password (loop until unlocked) -; Similar to mircryption's mc_requirepassphrase -alias fish11_require_masterkey { - var %is_unlocked = $dll(%Fish11DllFile, FiSH11_MasterKeyIsUnlocked, $null) - - while (%is_unlocked != yes) { - var %password = $input(Master key is locked. Enter password to unlock :, pvq, FiSH_11 Master Key Required, ) - - ; If user cancelled - if (%password == $null) { - echo $color(Error) -at *** FiSH_11: master key unlock is required. Cancelling operation. - return - } - - ; Try to unlock - var %result = $dll(%Fish11DllFile, FiSH11_MasterKeyUnlock, %password) - unset %password - - ; Check if now unlocked - %is_unlocked = $dll(%Fish11DllFile, FiSH11_MasterKeyIsUnlocked, $null) - - if (%is_unlocked == yes) { - echo $color(Mode text) -at *** FiSH_11: master key unlocked successfully - } - else { - echo $color(Error) -at *** FiSH_11: incorrect password. Try again. - } - } -} - -; === END MASTER KEY MANAGEMENT === - -; Show help and version information -alias fish11_help { - var %helpText - if ($dll(%Fish11DllFile, FiSH11_Help, &%helpText)) { - echo $color(Mode text) -at *** FiSH help: %helpText - } - else { - echo $color(Mode text) -at *** FiSH: help information unavailable - } - - ; Add Master Key help - echo $color(Mode text) -at $chr(160) - echo $color(Mode text) -at *** FiSH_11 Master Key commands: - echo $color(Mode text) -at *** /fish11_unlock [password] - Unlock master key (encrypts config/logs) - echo $color(Mode text) -at *** /fish11_lock - Lock master key (clears from memory) - echo $color(Mode text) -at *** /fish11_masterkey_status - Show master key status - echo $color(Mode text) -at *** When unlocked: configuration and logs are encrypted with Argon2id + ChaCha20-Poly1305 - echo $color(Mode text) -at *** When locked: configuration and logs are stored in plaintext - - ; Add FCEP-1 help - echo $color(Mode text) -at $chr(160) - echo $color(Mode text) -at *** FiSH_11 FCEP-1 (Channel Encryption v1) commands: - echo $color(Mode text) -at *** /fish11_initchannel <#channel> [nick2] ... - Initialize encrypted channel - echo $color(Mode text) -at *** Shorthand: /fcep or /chankey - echo $color(Mode text) -at *** Example: /fish11_initchannel #secret alice bob charlie - echo $color(Mode text) -at *** Note: all members must have pre-shared keys with you first - echo $color(Mode text) -at $chr(160) - echo $color(Mode text) -at *** FCEP-1 automatically decrypts incoming channel messages - echo $color(Mode text) -at *** Channel names are case-insensitive (#Secret = #secret) - - ; Add plaintext topic commands - echo $color(Mode text) -at $chr(160) - echo $color(Mode text) -at *** FiSH_11 Plaintext Topic commands: - echo $color(Mode text) -at *** /settopic <#channel> - Set a plaintext topic for a channel - echo $color(Mode text) -at *** /gettopic <#channel> - Get the plaintext topic for a channel - echo $color(Mode text) -at *** /removetopic <#channel> - Remove the plaintext topic for a channel - echo $color(Mode text) -at *** /etopic - Encrypt and set a topic for the current channel - echo $color(Mode text) -at $chr(160) - echo $color(Mode text) -at *** Plaintext topics are stored in the configuration file and can be retrieved later - - ; Add legacy fish10 topic commands - echo $color(Mode text) -at $chr(160) - echo $color(Mode text) -at *** FiSH_10 Legacy Topic commands: - echo $color(Mode text) -at *** /fish10_settopic <#channel> - Set a plaintext topic in legacy format - echo $color(Mode text) -at *** /fish10_gettopic <#channel> - Get a plaintext topic from legacy format - echo $color(Mode text) -at *** /fish10_removetopic <#channel> - Remove a plaintext topic from legacy format - echo $color(Mode text) -at $chr(160) - echo $color(Mode text) -at *** Legacy topics are stored in the configuration file with fish10 compatibility -} - - -alias fish11_version { - var %raw_version_info = $dll(%Fish11DllFile, FiSH11_GetVersion, $null) - - if (!%raw_version_info) { - echo -ts *** FiSH_11: ERROR - could not get version info from DLL. - return - } - - ; Parse the raw string: VERSION|BUILD_TYPE - ; 124 is ASCII for | - var %version_string = $gettok(%raw_version_info, 1, 124) - var %build_type = $gettok(%raw_version_info, 2, 124) - - ; Display the base version info - echo -ts *** %version_string *** - - ; Display context-specific warning or info message - if (%build_type == DEBUG) { - echo 4 -ts $chr(3)4 *** SECURITY WARNING : you're running a DEBUG version which logs EVERYTHING (keys, private messages, etc.) ON YOUR DISK. - echo 4 -ts $chr(3)4 *** DO NOT USE THIS VERSION IN REAL LIFE. - } - else { - echo 4 -ts $chr(3)3 *** You are running a RELEASE version. Sensitive data is NOT logged. - echo 4 -ts $chr(3)3 *** Logging can be configured in your fish_11.ini file. - } -} - - - -alias fish11_injection_version { - var %inject_version = $dll(%Fish11InjectDllFile, FiSH11_InjectVersion, $null) - echo -ts *** %inject_version *** -} - - - -; Debug functionality -alias fish11_debug { - var %w = @fishdebug - var %a = aline -ph %w - - var %f1 = fishdebug - var %f2 = $rand(0,9999) - ;var %x = $iif($isfile(%Fish11DllFile),$dll(%Fish11DllFile,FiSH11_SetKey,$+($network,%f1,%f2,HelloWorld)),MISSING_DLL) - var %x = $iif($isfile(%Fish11DllFile),$dll(%Fish11DllFile,FiSH11_SetKey,$+($network,%f1,%f2,HelloWorld)),MISSING_DLL) - - var %f11dll = $+(",$nofile($mircexe),fish_11.dll") - var %f11config = %fish_config_file - - if (!$window(%w)) { - window -a %w -1 -1 550 300 Courier New 12 - } - else { - clear %w - window -a %w - } - - %a ---------FISH DEBUG--------- - %a $cr - %a ::VERSION - %a mIRC version: $version - %a SSL version: $sslversion - %a SSL ready: $sslready - %a SSL mode: $iif($readini($mircini,ssl,load),$v1,default) - %a $cr - %a ::mIRC - %a mIRC dir: $mircdir - %a mIRC.exe: $mircexe - %a mIRC.ini: $mircini - %a Portable: $iif($readini($mircini,about,portable),$v1,NotFound) - %a $cr - %a ::Files - %a fish_11.dll: %Fish11DllFile - $isfile(%Fish11DllFile) - %a version string: $iif($isfile(%Fish11DllFile),$dll(%Fish11DllFile,FiSH11_GetVersion),NotFound) - %a fish_11.toml: %fish_config_file - $isfile(%fish_config_file) - %a $cr - %a ::INI Configuration - %a Process incoming: $fish11_GetIniValue(process_incoming) - %a Process outgoing: $fish11_GetIniValue(process_outgoing) - %a Plain prefix: $fish11_GetIniValue(plain_prefix) - %a Mark position: $fish11_GetIniValue(mark_position) - %a Encrypt notice: $fish11_GetIniValue(encrypt_notice) - %a Encrypt action: $fish11_GetIniValue(encrypt_action) - %a No fish10 legacy: $fish11_GetIniValue(no_fish10_legacy) - %a $cr - %a ::Variables - %a fish_config_file: %fish_config_file - %a FiSH_dll: %Fish11DllFile - %a $cr - %a ::Testing - %a >> Writing key to config - %a << Reading back key, you should see a 'HelloWorld' on the next line. - %a !! FileGetKey: $iif($dll(%Fish11DllFile,FiSH11_FileGetKey, $+($network," ",%f1," ",%f2)),$v1,NotFound) - %a << Deleting key from config - - var %delkey = $dll(%Fish11DllFile,FiSH11_FileDelKey,$+($network," ",%f1," ",%f2)) -} - - -; Debug: capture raw return from FiSH11_ExchangeKey and display hex/quoted output -alias fish11_debug_exchange { - if ($1 == $null) { echo 4 -a Usage: /fish11_debug_exchange | return } - var %nick = $1 - - ; Use $dll(...) to capture returned identifier string directly - var %raw_exch = $dll(%Fish11DllFile, FiSH11_ExchangeKey, %nick) - - ; Show quoted version so we can see whitespace/newlines - echo 4 -a *** FiSH_11 DEBUG: raw quoted return: $qt(%raw_exch) - - ; Replace common control chars with visible markers for quick glance - var %visible = $replace(%raw_exch, $chr(13) $+ $chr(10), , $chr(13), , $chr(10), , $chr(9), ) - echo 4 -a *** FiSH_11 DEBUG: visible: %visible - - ; Print decimal codes for each character (limit 200 chars to avoid flooding) - var %limited = $left(%raw_exch, 200) - var %codes = $null - var %i = 1 - while (%i <= $len(%limited)) { - var %c = $mid(%limited, %i, 1) - var %codes = %codes $+ $asc(%c) $+ " " - inc %i - } - echo 4 -a *** FiSH_11 DEBUG: decimal codes (first 200 chars): %codes -} - - - -; INI file viewer -alias fish11_ViewIniFile { - var %w = @iniviewer - - if (!$isfile(%fish_config_file)) { - echo $color(Mode text) -at *** FiSH: config file not found: %fish_config_file - return - } - - if (!$window(%w)) { - window -a %w -1 -1 550 500 Courier New 10 - } - else { - clear %w - window -a %w - } - - titlebar %w FiSH 11 Configuration - %fish_config_file - - var %i = 1 - while (%i <= $lines(%fish_config_file)) { - aline %w $read(%fish_config_file, %i) - inc %i - } -} - - - -; Helper functions -alias -l fishdebug.clip { - clipboard - var %i = 1 - while ($line(@fishdebug,%i)) { - clipboard -an $v1 - inc %i - } -} - - - -alias statusmsg { - echo 4 -s [FiSH_11] $1- -} - - - -; Safety function to prevent accidental key overwrites -alias fish11_setkey_safe { - var %target = $1 - var %existing_key = $dll(%Fish11DllFile, FiSH11_FileGetKey, %target) - - if ($len(%existing_key) > 1) { - if ($?!="Key already exists for %target $+ . Overwrite? (Yes/No)") { - fish11_setkey %target $2- - } - } - else { - fish11_setkey %target $2- - } -} - - - -; INI Helper Functions (for compatibility with older scripts) -alias INI_GetString { - ; Arguments: $1 = key, $2 = default value - var %config_path = $+($scriptdir,fish_11.ini) - var %value = $readini(%config_path, FiSH11, $1) - if (%value == $null) { return $2 } - return %value -} - - - -alias INI_GetInt { - ; Arguments: $1 = key, $2 = default value - var %config_path = $+($scriptdir,fish_11.ini) - var %value = $readini(%config_path, FiSH11, $1) - if (%value == $null) { return $2 } - return %value -} - - - -; === MENUS === - -; Menu for channel windows -menu channel { - - - FiSH_11 channel encryption - .Add a channel key encryption - ..Manual key : fish11_set_manual_key_dialog $chan - ..FCEP-1 key : fish11_init_fcep_dialog $chan - .Encrypt topic - ..Enable topic encryption :{ fish11_SetChannelIniValue $chan encrypt_topic 1 | echo $color(Mode text) -at *** FiSH: topic encryption enabled for $chan } - ..Disable topic encryption :{ fish11_SetChannelIniValue $chan encrypt_topic 0 | echo $color(Mode text) -at *** FiSH: topic encryption disabled for $chan } - .- - .Show channel key info : fish11_show_channel_key_info $chan - .Remove channel key : fish11_remove_channel_key $chan - .- - .Show key :fish11_showkey $chan - .Show fingerprint :fish11_showfingerprint $chan - .Copy fingerprint to clipboard :{ - fish11_showfingerprint $chan - var %fp = %fish11.lastfingerprint. $+ [ $chan ] - if (%fp != $null) { - clipboard %fp - echo $color(Mode text) -at *** FiSH_11: fingerprint for $chan copied to clipboard - } - } - .- - .Encrypt message :{ - var %msg = $?="Enter message to encrypt:" - if (%msg) { - var %encrypted = $fish11_encrypt($chan,%msg) - echo $color(Mode text) -at *** FiSH: encrypted message: %encrypted - } - } - .Decrypt message :{ - var %msg = $?="Enter message to decrypt:" - if (%msg) { - var %decrypted = $fish11_decrypt($chan,%msg) - echo $color(Mode text) -at *** FiSH: decrypted message: %decrypted - } - } - .Set topic (encrypted) :{ - var %topic = $?="Enter encrypted topic for " $+ $chan $+ ":" - if (%topic != $null) etopic %topic - } - .Set topic (plaintext) :{ - var %topic = $?="Enter plaintext topic for " $+ $chan $+ ":" - if (%topic != $null) settopic $chan %topic - } - .Get topic (plaintext) :{ - var %result = $gettopic($chan) - if (%result != $null) { - echo $color(Mode text) -at *** FiSH_11: Topic for $chan is: %result - } - } - - - FiSH_10 legacy (Blowfish) - .Show legacy key :fish10_showkey $chan - .Set legacy key... :{ var %key = $?="Enter hex Blowfish key (4-56 bytes):" | if (%key != $null) fish10_setkey $chan %key } - .Remove legacy key :fish10_delkey $chan -} - -; Menu for query windows -menu query { - - - FiSH_11 - .X25519 keyXchange: fish11_X25519_INIT $1 - .- - .Show key :fish11_showkey $1 - .Show fingerprint :fish11_showfingerprint $1 - .Copy fingerprint to clipboard :{ - fish11_showfingerprint $1 - var %fp = %fish11.lastfingerprint. $+ [ $1 ] - if (%fp != $null) { - clipboard %fp - echo $color(Mode text) -at *** FiSH_11: fingerprint for $1 copied to clipboard - } - } - .- - .Set manual key... :{ var %key = $?="Enter manual key for " $+ $1 $+ ":" | if (%key != $null) fish11_setkey_manual $1 %key } - .Set new key :{ var %key = $?="Enter new key for " $+ $1 $+ ":" | if (%key != $null) fish11_setkey $1 %key } - .Set new key (UTF-8) :{ var %key = $?="Enter new key for " $+ $1 $+ " (UTF-8):" | if (%key != $null) fish11_setkey_utf8 $1 %key } - .Remove key :fish11_removekey $1 - .- - .Encrypt message :{ - var %msg = $?="Enter message to encrypt:" - if (%msg) { - var %encrypted = $fish11_encrypt($1,%msg) - echo $color(Mode text) -at *** FiSH: encrypted message: %encrypted - } - } - .Decrypt message :{ - var %msg = $?="Enter message to decrypt:" - if (%msg) { - var %decrypted = $fish11_decrypt($1,%msg) - echo $color(Mode text) -at *** FiSH: decrypted message: %decrypted - } - } - - - FiSH_10 legacy (DH1080) - .DH1080 keyXchange: fish10_keyx $1 - .- - .Show legacy key :fish10_showkey $1 - .Set legacy key... :{ var %key = $?="Enter hex Blowfish key (4-56 bytes) for " $+ $1 $+ ":" | if (%key != $null) fish10_setkey $1 %key } - .Remove legacy key :fish10_delkey $1 -} - -; Menu for nicklist -menu nicklist { - - - FiSH_11 - .X25519 keyXchange: fish11_X25519_INIT $1 - .- - .Show key :fish11_showkey $1 - .Show fingerprint :fish11_showfingerprint $1 - .- - .Set manual key... :{ var %key = $?="Enter manual key for " $+ $1 $+ ":" | if (%key != $null) fish11_setkey_manual $1 %key } - .Set new key :{ var %key = $?="Enter new key for " $+ $1 $+ ":" | if (%key != $null) fish11_setkey $1 %key } - .Set new key (UTF-8) :{ var %key = $?="Enter new key for " $+ $1 $+ " (UTF-8):" | if (%key != $null) fish11_setkey_utf8 $1 %key } - .Remove key :fish11_removekey $1 - .Use same key as $chan :fish11_usechankey $1 $chan - .- - .Encrypt message :{ - var %msg = $?="Enter message to encrypt:" - if (%msg) { - var %encrypted = $fish11_encrypt($1,%msg) - echo $color(Mode text) -at *** FiSH: encrypted message: %encrypted - } - } - .Decrypt message :{ - var %msg = $?="Enter message to decrypt:" - if (%msg) { - var %decrypted = $fish11_decrypt($1,%msg) - echo $color(Mode text) -at *** FiSH: decrypted message: %decrypted - } - } - - - FiSH_10 legacy (DH1080) - .DH1080 keyXchange: fish10_keyx $1 - .- - .Show legacy key :fish10_showkey $1 - .Set legacy key... :{ var %key = $?="Enter hex Blowfish key (4-56 bytes) for " $+ $1 $+ ":" | if (%key != $null) fish10_setkey $1 %key } - .Remove legacy key :fish10_delkey $1 - .Use same legacy key as $chan :fish10_usechankey $1 $chan -} - -; Common menu available in all windows -menu status,channel,nicklist,query { - FiSH_11 - .Core version :fish11_version - .Injection version : fish11_injection_version - .Help :fish11_help - .- - .Master key - ..Unlock master key :fish11_unlock - ..Lock master key :fish11_lock - ..Show master key status :fish11_masterkey_status - .- - .Set topic (encrypted) :{ - ; Only allow in channel windows - if ($window($active).type != channel) { - echo $color(Mode text) -at *** FiSH_11: etopic can only be used in channel windows - return - } - var %topic = $?="Enter encrypted topic for " $+ $active $+ ":" - if (%topic != $null) etopic %topic - } - .Add channel key encryption :{ - ; Only allow in channel windows (more robust check) - if ($window($active).type != channel) { - echo $color(Mode text) -at *** FiSH_11: This command can only be used in channel windows - return - } - ; Open a dialog to choose encryption method - var %choice = $input(Add Channel Key Encryption for $active $+ :, pvq, FiSH_11 Add Channel Key) - if (%choice == $null) return - - if (%choice == 1) { - ; Set Manual Key - fish11_set_manual_key_dialog $active - } - elseif (%choice == 2) { - ; Set FCEP-1 Key - fish11_init_fcep_dialog $active - } - } - .List all keys :fish11_file_list_keys - .Test encryption :fish11_test_crypt - .- - .Set plain-prefix :{ var %prefix = $?="Enter new plain-prefix:" | if (%prefix != $null) fish11_prefix %prefix } - .Auto-KeyXchange $+ $chr(32) $+ %autokeyx - ..Enable :set %autokeyx [On] - ..Disable :set %autokeyx [Off] - .Misc config - ..Encrypt outgoing [Status] - ...Enable :{ fish11_SetIniValue process_outgoing 1 | echo $color(Mode text) -at *** FiSH: outgoing message encryption enabled } - ...Disable :{ fish11_SetIniValue process_outgoing 0 | echo $color(Mode text) -at *** FiSH: outgoing message encryption disabled } - ..Decrypt incoming [Status] - ...Enable :{ fish11_SetIniValue process_incoming 1 | echo $color(Mode text) -at *** FiSH: incoming message decryption enabled } - ...Disable :{ fish11_SetIniValue process_incoming 0 | echo $color(Mode text) -at *** FiSH: incoming message decryption disabled } - ..- - ..Crypt-mark (Incoming) - ...Prefix :{ fish11_SetIniValue mark_position 2 | echo $color(Mode text) -at *** FiSH: encryption mark set to prefix } - ...Suffix :{ fish11_SetIniValue mark_position 1 | echo $color(Mode text) -at *** FiSH: encryption mark set to suffix } - ...Disable :{ fish11_SetIniValue mark_position 0 | echo $color(Mode text) -at *** FiSH: encryption mark disabled } - ..Crypt-mark (Outgoing) $+ $chr(32) $+ %mark_outgoing - ...Enable :set %mark_outgoing [On] - ...Disable :set %mark_outgoing [Off] - ...- - ...Style 1 :{ - set %mark_style 1 - set %mark_outgoing [On] - echo $color(Mode text) -at *** FiSH: outgoing mark style set to 1 (suffix) - } - ...Style 2 :{ - set %mark_style 2 - set %mark_outgoing [On] - echo $color(Mode text) -at *** FiSH: outgoing mark style set to 2 (prefix) - } - ...Style 3 :{ - set %mark_style 3 - set %mark_outgoing [On] - echo $color(Mode text) -at *** FiSH: outgoing mark style set to 3 (colored brackets) - } - ..NickTracker $+ $chr(32) $+ %NickTrack - ...Enable :{ set %NickTrack [On] | echo $color(Mode text) -at *** FiSH: nick tracking enabled } - ...Disable :{ set %NickTrack [Off] | echo $color(Mode text) -at *** FiSH: nick tracking disabled } - ..Encrypt NOTICE [Status] - ...Enable :{ fish11_SetIniValue encrypt_notice 1 | echo $color(Mode text) -at *** FiSH: NOTICE encryption enabled } - ...Disable :{ fish11_SetIniValue encrypt_notice 0 | echo $color(Mode text) -at *** FiSH: NOTICE encryption disabled } - ..Encrypt ACTION [Status] - ...Enable :{ fish11_SetIniValue encrypt_action 1 | echo $color(Mode text) -at *** FiSH: ACTION encryption enabled } - ...Disable :{ fish11_SetIniValue encrypt_action 0 | echo $color(Mode text) -at *** FiSH: ACTION encryption disabled } - ..No legacy FiSH 10 [Status] - ...Enable :{ fish11_SetIniValue no_fish10_legacy 1 | echo $color(Mode text) -at *** FiSH: legacy FiSH 10 compatibility disabled } - ...Disable :{ fish11_SetIniValue no_fish10_legacy 0 | echo $color(Mode text) -at *** FiSH: legacy FiSH 10 compatibility enabled } - ..- - ..Open config file :fish11_ViewIniFile - ..- - ..FiSH_11 - secure IRC encryption :shell -o https://github.com/ggielly/fish_11 - .Backup and restore - ..Create backup now :fish11_ScheduleBackup - ..Restore from backup :{ - var %file = $sfile($+(",$mircdir,fish_11\backups\"),Restore FiSH keys from:,*.bak) - if (%file) { - if ($dll(%Fish11DllFile, FiSH11_RestoreKeys, %file)) { - echo $color(Mode text) -at *** FiSH: keys successfully restored from %file - } - else { - echo $color(Error) -at *** FiSH: failed to restore keys from %file - } - } - } - ..Schedule daily backup :{ - .timer.fish11.DailyBackup 0 86400 fish11_ScheduleBackup - echo $color(Mode text) -at *** FiSH: daily key backup scheduled - } - ..Stop scheduled backups :{ - .timers.fish11.DailyBackup off - echo $color(Mode text) -at *** FiSH: scheduled key backups stopped - } - .Debug - ..Show debug info :fish11_debug - ..View INI file :fish11_ViewIniFile - ..Show encryption stats :fish11_stats - - - - FiSH_10 legacy compatibility - .DH1080 key exchange :fish10_keyx $active - .Show legacy key :fish10_showkey $active - .Set legacy key... :{ var %key = $?="Enter hex Blowfish key (4-56 bytes):" | if (%key != $null) fish10_setkey $active %key } - .Remove legacy key :fish10_delkey $active - .- - .Set topic (encrypted) :{ - ; Only allow in channel windows - if ($window($active).type != channel) { - echo $color(Mode text) -at *** FiSH_10: etopic can only be used in channel windows - return - } - var %topic = $?="Enter encrypted topic for " $+ $active $+ ":" - if (%topic != $null) etopic %topic - } - .- - .About FiSH_10 compatibility :{ - echo $color(Mode text) -at *** FiSH_10 Legacy Compatibility - echo $color(Mode text) -at *** Supports DH1080 key exchange and Blowfish ECB encryption - echo $color(Mode text) -at *** Compatible with mIRC FiSH 10.x and other FiSH implementations - echo $color(Mode text) -at *** Use DH1080 for automatic key exchange or set keys manually - } -} - - - -; Window context menus -menu @fishdebug { - &Copy to Clipboard: fishdebug.clip - - - &Refresh:{ clear @fishdebug | fish11_debug } - C&lose:{ window -c @fishdebug } -} - - - -menu @iniviewer { - &Save Changes:{ - var %temp_file = $+($mircdir, fish_11.tmp) - var %backup_file = %fish_config_file $+ .bak - - ; Write the content of the window to a temporary file - .remove %temp_file - var %i = 1 - while (%i <= $line(@iniviewer, 0)) { - write %temp_file $line(@iniviewer, %i) - inc %i - } - - if (!$isfile(%temp_file)) { - echo $color(Error) -at *** FiSH: error writing temporary file. Save aborted. - return - } - - ; Safely replace the old config file with the new one - .rename %fish_config_file %backup_file - .rename %temp_file %fish_config_file - - if ($isfile(%fish_config_file)) { - echo $color(Mode text) -at *** FiSH: configuration saved - .remove %backup_file - } else { - echo $color(Error) -at *** FiSH: error saving config, restoring from backup. - .rename %backup_file %fish_config_file - } - } - &Refresh:{ - clear @iniviewer - var %i = 1 - while (%i <= $lines(%fish_config_file)) { - aline @iniviewer $read(%fish_config_file, %i) - inc %i - } - } - - - C&lose:{ window -c @iniviewer } -} - - - -; === ALIAS SHORTCUTS FOR USER COMMANDS === -; These provide simplified commands for users - -; Encrypted topic alias - transparently encrypts the topic via the injection hook -alias etopic { - ; Check if we're in a channel window - if ($window($active).type != channel) { - echo $color(Mode text) -at *** FiSH_11: etopic can only be used in channel windows - return - } - - ; Check if there's a key for this channel (both traditional and FCEP-1) - var %channelKey = $dll(%Fish11DllFile, FiSH11_FileGetKey, $active) - ; Robust error check: if it doesn't look like a valid key or starts with error indicators - if ($left(%channelKey, 6) == Error: || $left(%channelKey, 3) == no ) { set %channelKey $null } - - var %hasLegacyKey = $dll(%Fish11DllFile, FiSH10_HasKey, $active) - - if (%channelKey == $null && %hasLegacyKey != 1) { - ; No key exists, but the engine may still try to encrypt if a channel key exists - ; This will be handled by the engine registration code - echo $color(Mode text) -at *** FiSH_11: no encryption key found for $active, topic will be sent in plain text - } else { - echo $color(Mode text) -at *** FiSH_11: topic will be encrypted for $active $iif(%hasLegacyKey == 1, (FiSH 10 legacy)) - } - - ; Execute the topic command - encryption will be handled by the engine - /topic $1- - - ; Clean up variables - unset %channelKey -} - -; Set a plaintext topic for a channel -alias settopic { - if ($1 == $null || $2- == $null) { - echo 4 -a Syntax: /settopic <#channel> - return - } - - var %channel = $1 - var %topic = $2- - - ; Validate channel name - if (!$regex(%channel, /^[#&]/)) { - echo $color(Error) -at *** FiSH_11 ERROR: Invalid channel name %channel (must start with # or &) - return - } - - var %result = $dll(%Fish11DllFile, FiSH11_SetTopic, $+(%channel, $chr(32), %topic)) - - if (%result && $left(%result, 6) != Error:) { - echo $color(Mode text) -at *** FiSH_11: %result - } - else { - var %error_msg = $iif(%result, %result, "Unknown error - could not set topic for %channel") - echo $color(Error) -at *** FiSH_11: error setting topic for %channel - %error_msg - } -} - -; Get a plaintext topic for a channel -alias gettopic { - if ($1 == $null) { - echo 4 -a Syntax: /gettopic <#channel> - return - } - - var %channel = $1 - - ; Validate channel name - if (!$regex(%channel, /^[#&]/)) { - echo $color(Error) -at *** FiSH_11 ERROR: Invalid channel name %channel (must start with # or &) - return - } - - var %result = $dll(%Fish11DllFile, FiSH11_GetTopic, %channel) - - if (%result && $left(%result, 6) != Error:) { - echo $color(Mode text) -at *** FiSH_11: Topic for %channel is: %result - } - else { - var %error_msg = $iif(%result, %result, "Unknown error - could not get topic for %channel") - echo $color(Error) -at *** FiSH_11: error getting topic for %channel - %error_msg - } -} - -; Remove a plaintext topic for a channel -alias removetopic { - if ($1 == $null) { - echo 4 -a Syntax: /removetopic <#channel> - return - } - - var %channel = $1 - - ; Validate channel name - if (!$regex(%channel, /^[#&]/)) { - echo $color(Error) -at *** FiSH_11 ERROR: Invalid channel name %channel (must start with # or &) - return - } - - var %result = $dll(%Fish11DllFile, FiSH11_RemoveTopic, %channel) - - if (%result && $left(%result, 6) != Error:) { - echo $color(Mode text) -at *** FiSH_11: %result - } - else { - var %error_msg = $iif(%result, %result, "Unknown error - could not remove topic for %channel") - echo $color(Error) -at *** FiSH_11: error removing topic for %channel - %error_msg - } -} - -alias fish_genkey11 { fish11_setkey_safe $1 $2- } -alias fish_setkey11 { fish11_setkey $1 $2- } -alias fish_getkey11 { fish11_showkey $1 } -alias fish_fp11 { fish11_showfingerprint $1- } -alias fish_delkey11 { fish11_removekey $1 } -alias fish_listkeys11 { fish11_file_list_keys } -alias fish_encrypt11 { return $fish11_encrypt($1, $2-) } -alias fish_decrypt11 { return $fish11_decrypt($1, $2-) } -alias fish_keyx11 { fish11_X25519_INIT $1 } -alias fish_keyp11 { fish11_ProcessPublicKey $1 $2- } -alias fish_test11 { fish11_test_crypt $1- } -alias fish_help11 { fish11_help } -alias fish_version11 { fish11_version } -alias fish_initchannel11 { fish11_initchannel $1- } - -; Short alias for statistics -alias fish_stats11 { fish11_stats } - -; Legacy FiSH 10 aliases -alias fish10_setkey { - if ($1 == $null || $2 == $null) { - echo 4 -a Syntax: /fish10_setkey - return - } - var %msg = $dll(%Fish11DllFile, FiSH10_SetKey, $1 $2-) - if (%msg && $left(%msg, 6) != Error:) { - echo -a *** FiSH_10: %msg - } - else { - echo -a *** FiSH_10: error setting key - %msg - } -} - -; Set a plaintext topic for a channel in the legacy fish10 section -alias fish10_settopic { - if ($1 == $null || $2- == $null) { - echo 4 -a Syntax: /fish10_settopic <#channel> - return - } - - var %channel = $1 - var %topic = $2- - - ; Validate channel name - if (!$regex(%channel, /^[#&]/)) { - echo $color(Error) -at *** FiSH_10 ERROR: Invalid channel name %channel (must start with # or &) - return - } - - var %result = $dll(%Fish11DllFile, FiSH10_SetTopic, $+(%channel, $chr(32), %topic)) - - if (%result && $left(%result, 6) != Error:) { - echo $color(Mode text) -at *** FiSH_10: %result - } - else { - var %error_msg = $iif(%result, %result, "Unknown error - could not set topic for %channel") - echo $color(Error) -at *** FiSH_10: error setting topic for %channel - %error_msg - } -} - -; Get a plaintext topic for a channel from the legacy fish10 section -alias fish10_gettopic { - if ($1 == $null) { - echo 4 -a Syntax: /fish10_gettopic <#channel> - return - } - - var %channel = $1 - - ; Validate channel name - if (!$regex(%channel, /^[#&]/)) { - echo $color(Error) -at *** FiSH_10 ERROR: Invalid channel name %channel (must start with # or &) - return - } - - var %result = $dll(%Fish11DllFile, FiSH10_GetTopic, %channel) - - if (%result && $left(%result, 6) != Error:) { - echo $color(Mode text) -at *** FiSH_10: Topic for %channel is: %result - } - else { - var %error_msg = $iif(%result, %result, "Unknown error - could not get topic for %channel") - echo $color(Error) -at *** FiSH_10: error getting topic for %channel - %error_msg - } -} - -; Remove a plaintext topic for a channel from the legacy fish10 section -alias fish10_removetopic { - if ($1 == $null) { - echo 4 -a Syntax: /fish10_removetopic <#channel> - return - } - - var %channel = $1 - - ; Validate channel name - if (!$regex(%channel, /^[#&]/)) { - echo $color(Error) -at *** FiSH_10 ERROR: Invalid channel name %channel (must start with # or &) - return - } - - var %result = $dll(%Fish11DllFile, FiSH10_RemoveTopic, %channel) - - if (%result && $left(%result, 6) != Error:) { - echo $color(Mode text) -at *** FiSH_10: %result - } - else { - var %error_msg = $iif(%result, %result, "Unknown error - could not remove topic for %channel") - echo $color(Error) -at *** FiSH_10: error removing topic for %channel - %error_msg - } -} - -alias fish10_delkey { - if ($1 == $null) var %target = $active - else var %target = $1 - var %msg = $dll(%Fish11DllFile, FiSH10_DelKey, %target) - if (%msg && $left(%msg, 6) != Error:) { - echo -a *** FiSH_10: %msg - } - else { - echo -a *** FiSH_10: error removing key - %msg - } -} - -alias fish10_showkey { - if ($1 == $null) var %target = $active - else var %target = $1 - var %key = $dll(%Fish11DllFile, FiSH10_GetKey, %target) - if ($left(%key, 6) == Error:) { - echo -a *** FiSH_10: error retrieving key for %target : %key - } - elseif (%key == $null) { - echo -a *** FiSH_10: no key found for %target - } else { - echo -a *** FiSH_10: key for %target : %key - } -} - -alias fish10_usechankey { - if ($1 == $null || $2 == $null) { - echo 4 -a Syntax: /fish10_usechankey - return - } - - var %target = $1 - var %source = $2 - - ; Get the key from the source - var %key = $dll(%Fish11DllFile, FiSH11_FileGetKey, %source) - - if (%key == $null || $len(%key) < 4) { - echo $color(Error) -at *** FiSH_10: no valid key found for %source - return - } - - ; Set the same key for the target - fish10_setkey %target %key - echo $color(Mode text) -at *** FiSH_10: using same key as %source for %target -} - -alias fish10_keyx { - if ($1 == $null) var %target = $active - else var %target = $1 - - ; Use hash table to track in-progress exchanges - hadd -m fish10.dh %target 1 - - var %pub = $dll(%Fish11DllFile, FiSH10_DH1080_GenerateKeyPair, %target) - - ; Check if key generation was successful (should be a base64 string ending with 'A') - ; DH1080 public keys are ~181 chars long and end with 'A' - if ($len(%pub) > 100 && $right(%pub, 1) == A) { - .notice %target DH1080_INIT %pub - echo $color(Mode text) -tm %target *** FiSH_10: sent DH1080_INIT to %target $+ , waiting for reply... - - ; Set timeout timer - if (%KEY_EXCHANGE_TIMEOUT_SECONDS == $null) { var %timeout = 10 } - else { var %timeout = %KEY_EXCHANGE_TIMEOUT_SECONDS } - .timer 1 %timeout fish10_timeout_keyexchange %target - } - else { - hdel fish10.dh %target - echo $color(Error) -at *** FiSH_10: DH1080 init failed - %pub - } -} - -; Handle key exchange timeout for FiSH 10/DH1080 -alias fish10_timeout_keyexchange { - if ($1 == $null) return - var %contact = $1 - - ; Check if key exchange is still in progress - if ($hget(fish10.dh, %contact) == 1) { - hdel fish10.dh %contact - echo $color(Mode text) -at *** FiSH_10: key exchange with %contact timed out - } -} - -; Legacy DH1080 Notice Handlers -on ^*:NOTICE:DH1080_INIT*:?:{ - ; In mIRC NOTICE events: - ; $1 = "DH1080_INIT" - ; $2 = public key (base64) - ; $3 = "CBC" (optional) - var %their_pub = $2 - - ; Validate format: DH1080 public keys should be base64 - if (!$regex(%their_pub, /^[A-Za-z0-9+\/=]+$/)) { - echo $color(Error) -tm $nick *** FiSH_10: received invalid DH1080_INIT format from $nick - halt - } - - echo $color(Mode text) -tm $nick *** FiSH_10: received DH1080_INIT from $nick, responding... - - var %our_pub = $dll(%Fish11DllFile, FiSH10_DH1080_GenerateKeyPair, $nick) - - ; Check if key generation failed - if ($left(%our_pub, 6) == Error:) { - echo $color(Error) -tm $nick *** FiSH_10: key generation failed - %our_pub - halt - } - - var %secret = $dll(%Fish11DllFile, FiSH10_DH1080_ComputeSecret, $nick %their_pub) - - ; Check if secret computation failed - if ($left(%secret, 6) == Error:) { - echo $color(Error) -tm $nick *** FiSH_10: key exchange failed - %secret - halt - } - - ; The DLL automatically stores the shared secret as a Blowfish key for this nick - - ; Send DH1080_FINISH response with our public key - .notice $nick DH1080_FINISH %our_pub - - echo $color(Mode text) -tm $nick *** FiSH_10: key exchange complete with $nick - echo $color(Error) -tm $nick *** FiSH_10 WARNING: key exchange complete, but the identity of $nick is NOT VERIFIED. - halt -} - -on ^*:NOTICE:DH1080_FINISH*:?:{ - ; Verify an exchange is in progress - if ($hget(fish10.dh, $nick) != 1) { - echo -at *** FiSH_10: received DH1080_FINISH but no key exchange was in progress with $nick - halt - } - - ; In mIRC NOTICE events: - ; $1 = "DH1080_FINISH" - ; $2 = public key (base64) - var %their_pub = $2 - - ; Validate format - if (!$regex(%their_pub, /^[A-Za-z0-9+\/=]+$/)) { - echo $color(Error) -tm $nick *** FiSH_10: received invalid DH1080_FINISH format from $nick - hdel fish10.dh $nick - halt - } - - var %secret = $dll(%Fish11DllFile, FiSH10_DH1080_ComputeSecret, $nick %their_pub) - - ; Clean up tracking - hdel fish10.dh $nick - - ; Check if secret computation failed - if ($left(%secret, 6) == Error:) { - echo $color(Error) -tm $nick *** FiSH_10: key exchange failed - %secret - halt - } - - ; The DLL automatically stores the shared secret as a Blowfish key for this nick - - echo $color(Mode text) -tm $nick *** FiSH_10: key exchange complete with $nick - echo $color(Error) -tm $nick *** FiSH_10 WARNING: key exchange complete, but the identity of $nick is NOT VERIFIED. - halt -} - -; Direct command for channel encryption settings -alias fish11_channel_settings { - ; Check if we're in a channel window (more robust check) - if ($window($active).type != channel) { - echo $color(Mode text) -at *** FiSH_11: This command can only be used in channel windows - return - } - - ; Open a dialog to choose encryption method - var %choice = $input(Add Channel Key Encryption for $active $+ :, pvq, FiSH_11 Add Channel Key) - if (%choice == $null) return - - if (%choice == 1) { - ; Set Manual Key - fish11_set_manual_key_dialog $active - } - elseif (%choice == 2) { - ; Set FCEP-1 Key - fish11_init_fcep_dialog $active - } -} - -; Short alias for channel settings -alias fcs { fish11_channel_settings } - -; Boîte de dialogue pour la clé manuelle -alias fish11_set_manual_key_dialog { - ; Vérifier si nous sommes dans une fenêtre de canal (méthode plus robuste) - if ($window($active).type != channel) { - echo $color(Error) -at *** FiSH_11: Manual key can only be set for channels. Current window: $active (type: $window($active).type) - return - } - - var %channel = $active - var %key = $input(Enter 44-character base64 manual key for %channel $+ :, pvq, FiSH_11 Manual Channel Key) - - if (%key != $null) { - fish11_setkey_manual %channel %key - } -} - -; Boîte de dialogue pour FCEP-1 -alias fish11_init_fcep_dialog { - ; Vérifier si nous sommes dans une fenêtre de canal (méthode plus robuste) - if ($window($active).type != channel) { - echo $color(Error) -at *** FiSH_11: FCEP-1 key can only be set for channels. Current window: $active (type: $window($active).type) - return - } - - var %channel = $active - var %members = $input(Enter members to invite (space-separated) for %channel $+ :, pvq, FiSH_11 FCEP-1 Channel Setup) - - if (%members != $null) { - fish11_initchannel %channel %members - } -} - -; Afficher les informations sur la clé du canal -alias fish11_show_channel_key_info { - var %channel = $1 - - ; Vérifier si on a une clé manuelle - var %hasManualKey = $dll(%Fish11DllFile, FiSH11_HasManualChannelKey, %channel) - - ; Vérifier si on a une clé FCEP/ratchet - var %hasRatchetKey = $dll(%Fish11DllFile, FiSH11_HasRatchetChannelKey, %channel) - - ; Vérifier si le chiffrement des topics est activé - var %encryptTopic = $fish11_GetChannelIniValue(%channel, encrypt_topic) - - window -dCo +l @FiSH-ChannelInfo -1 -1 400 150 - titlebar @FiSH-ChannelInfo Channel Encryption Info for %channel - - aline @FiSH-ChannelInfo Channel: %channel - aline @FiSH-ChannelInfo $chr(160) - aline @FiSH-ChannelInfo Manual Key: $iif(%hasManualKey == 1, 🔒 Set, 🔓 Not set) - aline @FiSH-ChannelInfo FCEP/Ratchet Key: $iif(%hasRatchetKey == 1, 🔒 Set, 🔓 Not set) - aline @FiSH-ChannelInfo Topic Encryption: $iif(%encryptTopic == 1, ✅ Enabled, ❌ Disabled) - aline @FiSH-ChannelInfo $chr(160) - - if (%hasManualKey == 1 || %hasRatchetKey == 1) { - aline @FiSH-ChannelInfo Status: 🔒 Channel encryption is ACTIVE - aline @FiSH-ChannelInfo All messages and topics will be encrypted - } - else { - aline @FiSH-ChannelInfo Status: 🔓 Channel encryption is INACTIVE - aline @FiSH-ChannelInfo Messages and topics will be sent in plain text - } - - button @FiSH-ChannelInfo "Close", 1, 150 120 100 25 - var %result = $input(,pv,@FiSH-ChannelInfo) - window -c @FiSH-ChannelInfo -} - -; Supprimer la clé du canal -alias fish11_remove_channel_key { - var %channel = $1 - - ; Supprimer la clé manuelle si elle existe - $dll(%Fish11DllFile, FiSH11_RemoveManualChannelKey, %channel) - - ; Supprimer la clé ratchet si elle existe - $dll(%Fish11DllFile, FiSH11_RemoveRatchetChannelKey, %channel) - - ; Désactiver le chiffrement des topics - fish11_SetChannelIniValue %channel encrypt_topic 0 - - echo $color(Mode text) -at *** FiSH_11: All encryption keys removed for %channel -} - - -alias fcep11 { fish11_initchannel $1- } - -; Show encryption statistics -alias fish11_stats { - var %stats = $dll(%Fish11DllFile, FiSH11_GetEncryptionStats, $null) - if (%stats) { - echo $color(Mode text) -at *** FiSH_11 Encryption Statistics: - echo $color(Mode text) -at %stats - } - else { - echo $color(Error) -at *** FiSH_11: failed to retrieve encryption statistics - } -} - -; Short alias for statistics -alias fish_stats { fish11_stats } - +; +; This is the main loader script that loads all FiSH_11 modules. +; Modules are located in the 'modules' subdirectory. + +; === LOAD ALL MODULES === +; Load modules in dependency order +load -rs scripts\mirc\modules\fish11_startup.mrc +load -rs scripts\mirc\modules\fish11_key_management.mrc +load -rs scripts\mirc\modules\fish11_key_exchange.mrc +load -rs scripts\mirc\modules\fish11_encrypt_out.mrc +load -rs scripts\mirc\modules\fish11_encrypt_in.mrc +load -rs scripts\mirc\modules\fish11_channel.mrc +load -rs scripts\mirc\modules\fish11_masterkey.mrc +load -rs scripts\mirc\modules\fish11_legacy.mrc +load -rs scripts\mirc\modules\fish11_menus.mrc +load -rs scripts\mirc\modules\fish11_utils.mrc diff --git a/scripts/mirc/modules/fish11_channel.mrc b/scripts/mirc/modules/fish11_channel.mrc new file mode 100644 index 0000000..29596b9 --- /dev/null +++ b/scripts/mirc/modules/fish11_channel.mrc @@ -0,0 +1,283 @@ +;******************************* +;* FiSH_11 Channel Encryption * +;******************************* +; Channel encryption (FCEP-1) and manual channel key management +; Written by GuY, 2025. Licensed under GPL-v3. + +; === CHANNEL JOIN HANDLING === +on *:JOIN:#:{ + ; Only process our own joins + if ($nick != $me) return + + ; Get channel key if it exists + var %theKey = $dll(%Fish11DllFile, FiSH11_FileGetKey, $chan) + if (%theKey != $null) { + echo $color(Mode text) -at *** FiSH_11: found encryption key for $chan + + ; Check if topic encryption is enabled for this channel + var %encryptTopic = $fish11_GetChannelIniValue($chan, encrypt_topic) + if (%encryptTopic == 1) { + echo $color(Mode text) -at *** FiSH_11: topic encryption enabled for $chan + } + } + unset %theKey +} + + +; === FCEP-1 CHANNEL ENCRYPTION PROTOCOL HANDLERS === +; FCEP-1 (FiSH-11 Channel Encryption Protocol) enables secure multi-party +; channel encryption using a hub-and-spoke model where a coordinator distributes +; a shared channel key to all participants via their pre-established pairwise keys. + +on ^*:NOTICE:+FiSH-CEP-KEY*:?:{ + ; This event triggers when receiving a channel key distribution message. + ; Format: +FiSH-CEP-KEY <#channel> + ; $1 = +FiSH-CEP-KEY + ; $2 = #channel + ; $3 = coordinator_nick (claimed sender) + ; $4 = base64_wrapped_key + + var %num_tokens = $numtok($1-, 32) + + ; Validate message format + if (%num_tokens < 4) { + echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FISH_11 : FCEP-1 ERROR : Invalid +FiSH-CEP-KEY format from $nick (expected 4 tokens, got %num_tokens $+ ) + halt + } + + var %channel = $2 + var %coordinator = $3 + var %wrapped_key = $4 + + ; SECURITY: Verify sender authenticity + ; The actual IRC sender ($nick) MUST match the claimed coordinator + ; This prevents impersonation attacks where an attacker sends a +FiSH-CEP-KEY + ; message claiming to be from a trusted user + if ($nick != %coordinator) { + echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FiSH_11 : FCEP-1 SECURITY WARNING : key distribution from $nick claims to be from %coordinator - REJECTED + echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FiSH_11 : FCEP-1 : this may indicate an impersonation attack attempt! + halt + } + + ; Validate channel name format + if (!$regex(%channel, /^[#&]/)) { + echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FiSH_11 : FCEP-1 ERROR : invalid channel name format: %channel (must start with # or &) + halt + } + + ; Verify we have a pre-shared key with the coordinator + var %existing_key = $dll(%Fish11DllFile, FiSH11_FileGetKey, %coordinator) + + ; Check for errors or empty/missing key + if ($left(%existing_key, 6) == Error: || $len(%existing_key) < 4) { + if ($left(%existing_key, 6) == Error:) { + echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FiSH_11 : FCEP-1 ERROR: $right(%existing_key, $calc($len(%existing_key) - 6)) + } + else { + echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FiSH_11 : FCEP-1 ERROR: no pre-shared key found for coordinator %coordinator + echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FiSH_11 : FCEP-1 : you must establish a key with %coordinator first using /fish11_X25519_INIT %coordinator + } + halt + } + + ; Process the channel key via DLL + ; DLL will: decode base64, verify sender, unwrap key with pre-shared key, store channel key + var %result = $dll(%Fish11DllFile, FiSH11_ProcessChannelKey, %channel %coordinator $nick %wrapped_key) + + ; Check for success (DLL returns raw message, check if it's an error) + if (%result && $left(%result, 6) != Error:) { + ; Success - display confirmation message + echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FiSH_11 : FCEP-1 : %result + } + else { + ; Error occurred - display error from DLL + echo -s $chr(9) $+ $chr(160) $+ $chr(9604) FiSH_11 : FCEP-1 ERROR: %result + } + + halt +} + + +; === FCEP-1 CHANNEL ENCRYPTION COMMANDS === + +; Initialize channel encryption by generating and distributing a channel key +; Usage: /fish11_initchannel <#channel> [nick2] [nick3] ... +; Example: /fish11_initchannel #secret alice bob charlie +alias fish11_initchannel { + if ($1 == $null || $2 == $null) { + echo $color(Error) -at *** FiSH_11 FCEP-1: Usage: /fish11_initchannel <#channel> [nick2] ... + echo $color(Mode text) -at *** FiSH_11 FCEP-1: Example: /fish11_initchannel #secret alice bob charlie + echo $color(Mode text) -at *** FiSH_11 FCEP-1: Note: You must have pre-shared keys with all listed members (use /fish11_X25519_INIT first) + return + } + + var %channel = $1 + var %members = $2- + + ; Validate channel name + if (!$regex(%channel, /^[#&]/)) { + echo $color(Error) -at *** FiSH_11 FCEP-1 ERROR: Invalid channel name %channel (must start with # or &) + return + } + + ; Confirm action with user + echo $color(Mode text) -at *** FiSH_11 FCEP-1: Generating channel key for %channel + echo $color(Mode text) -at *** FiSH_11 FCEP-1: Members to receive key: %members + + ; Call DLL to generate key and create distribution commands + var %result = $dll(%Fish11DllFile, FiSH11_InitChannelKey, %channel %members) + + ; Check for errors (DLL returns "Error: ..." for errors) + if ($left(%result, 6) == Error:) { + echo $color(Error) -at *** FiSH_11 FCEP-1 ERROR: %result + return + } + + ; Parse result: commands are separated by | and last item is confirmation message + var %num_parts = $numtok(%result, 124) + var %i = 1 + var %has_commands = $false + + ; Execute all commands (parts starting with /) + while (%i <= %num_parts) { + var %part = $gettok(%result, %i, 124) + if ($left(%part, 1) == /) { + ; Security: Validate that this is an expected command before executing + if ($left(%part, 8) == /notice ) { + ; Only allow notice commands which are expected for FCEP-1 + %part + %has_commands = $true + } + else { + echo $color(Error) -at *** FiSH_11 FCEP-1: SECURITY WARNING - unexpected command from DLL: %part + } + } + else { + ; This is the confirmation message - display it + echo $color(Mode text) -at *** FiSH_11 FCEP-1: %part + } + inc %i + } + + ; If no commands were found, something went wrong + if (!%has_commands) { + echo $color(Error) -at *** FiSH_11 FCEP-1 ERROR: No distribution commands generated + } +} + +; Shorthand aliases for channel encryption +alias fcep { fish11_initchannel $1- } +alias chankey { fish11_initchannel $1- } +alias fcep11 { fish11_initchannel $1- } + + +; === CHANNEL SETTINGS DIALOG === +; Direct command for channel encryption settings +alias fish11_channel_settings { + ; Check if we're in a channel window (more robust check) + if ($window($active).type != channel) { + echo $color(Mode text) -at *** FiSH_11: This command can only be used in channel windows + return + } + + ; Open a dialog to choose encryption method + var %choice = $input(Add Channel Key Encryption for $active $+ :, pvq, FiSH_11 Add Channel Key) + if (%choice == $null) return + + if (%choice == 1) { + ; Set Manual Key + fish11_set_manual_key_dialog $active + } + elseif (%choice == 2) { + ; Set FCEP-1 Key + fish11_init_fcep_dialog $active + } +} + +; Short alias for channel settings +alias fcs { fish11_channel_settings } + +; Dialog for manual key +alias fish11_set_manual_key_dialog { + ; Check if we're in a channel window (more robust check) + if ($window($active).type != channel) { + echo $color(Error) -at *** FiSH_11: Manual key can only be set for channels. Current window: $active (type: $window($active).type) + return + } + + var %channel = $active + var %key = $input(Enter 44-character base64 manual key for %channel $+ :, pvq, FiSH_11 Manual Channel Key) + + if (%key != $null) { + fish11_setkey_manual %channel %key + } +} + +; Dialog for FCEP-1 +alias fish11_init_fcep_dialog { + ; Check if we're in a channel window (more robust check) + if ($window($active).type != channel) { + echo $color(Error) -at *** FiSH_11: FCEP-1 key can only be set for channels. Current window: $active (type: $window($active).type) + return + } + + var %channel = $active + var %members = $input(Enter members to invite (space-separated) for %channel $+ :, pvq, FiSH_11 FCEP-1 Channel Setup) + + if (%members != $null) { + fish11_initchannel %channel %members + } +} + +; Display channel key information +alias fish11_show_channel_key_info { + var %channel = $1 + + ; Check if we have a manual key + var %hasManualKey = $dll(%Fish11DllFile, FiSH11_HasManualChannelKey, %channel) + + ; Check if we have a FCEP/ratchet key + var %hasRatchetKey = $dll(%Fish11DllFile, FiSH11_HasRatchetChannelKey, %channel) + + ; Check if topic encryption is enabled + var %encryptTopic = $fish11_GetChannelIniValue(%channel, encrypt_topic) + + window -dCo +l @FiSH-ChannelInfo -1 -1 400 150 + titlebar @FiSH-ChannelInfo Channel Encryption Info for %channel + + aline @FiSH-ChannelInfo Channel: %channel + aline @FiSH-ChannelInfo $chr(160) + aline @FiSH-ChannelInfo Manual Key: $iif(%hasManualKey == 1, Set, Not set) + aline @FiSH-ChannelInfo FCEP/Ratchet Key: $iif(%hasRatchetKey == 1, Set, Not set) + aline @FiSH-ChannelInfo Topic Encryption: $iif(%encryptTopic == 1, Enabled, Disabled) + aline @FiSH-ChannelInfo $chr(160) + + if (%hasManualKey == 1 || %hasRatchetKey == 1) { + aline @FiSH-ChannelInfo Status: Channel encryption is ACTIVE + aline @FiSH-ChannelInfo All messages and topics will be encrypted + } + else { + aline @FiSH-ChannelInfo Status: Channel encryption is INACTIVE + aline @FiSH-ChannelInfo Messages and topics will be sent in plain text + } + + button @FiSH-ChannelInfo "Close", 1, 150 120 100 25 + var %result = $input(,pv,@FiSH-ChannelInfo) + window -c @FiSH-ChannelInfo +} + +; Remove channel key +alias fish11_remove_channel_key { + var %channel = $1 + + ; Remove manual key if it exists + noop $dll(%Fish11DllFile, FiSH11_RemoveManualChannelKey, %channel) + + ; Remove ratchet key if it exists + noop $dll(%Fish11DllFile, FiSH11_RemoveRatchetChannelKey, %channel) + + ; Disable topic encryption + fish11_SetChannelIniValue %channel encrypt_topic 0 + + echo $color(Mode text) -at *** FiSH_11: All encryption keys removed for %channel +} diff --git a/scripts/mirc/modules/fish11_encrypt_in.mrc b/scripts/mirc/modules/fish11_encrypt_in.mrc new file mode 100644 index 0000000..91a8104 --- /dev/null +++ b/scripts/mirc/modules/fish11_encrypt_in.mrc @@ -0,0 +1,208 @@ +;******************************* +;* FiSH_11 Incoming Decryption * +;******************************* +; Fallback handlers for incoming encrypted messages. +; NOTE: The fish_11_inject.dll handles most incoming decryption +; transparently by hooking SSL_Read. These handlers provide a +; fallback for cases where the injection DLL is not loaded. +; Written by GuY, 2025. Licensed under GPL-v3. + +; === HELPER: Decrypt incoming message === +; Attempts to decrypt an encrypted message from a sender +; Returns decrypted message or original message if decryption fails +alias -l fish11_try_decrypt_incoming { + var %sender = $1 + var %message = $2- + + ; Check if process_incoming is enabled + var %process_incoming = $dll(%Fish11DllFile, INI_GetBool, process_incoming 1) + if (%process_incoming == 0) return %message + + ; Get the encryption prefix (default: +FiSH) + var %prefix = $dll(%Fish11DllFile, INI_GetString, encryption_prefix +FiSH) + + ; Check if message starts with the encryption prefix + if ($left(%message, $len(%prefix)) != %prefix) return %message + + ; Extract the encrypted data (after prefix + space) + var %encrypted_data = $mid(%message, $calc($len(%prefix) + 2)) + + ; Try FiSH 11 decryption first + var %decrypted = $dll(%Fish11DllFile, FiSH11_DecryptMsg, $+(%sender, $chr(32), %prefix, $chr(32), %encrypted_data)) + + ; Check if decryption was successful (no error message) + if (%decrypted != $null && $left(%decrypted, 6) != Error:) { + return %decrypted + } + + ; Try FiSH 10 legacy decryption + var %decrypted = $dll(%Fish11DllFile, FiSH10_DecryptMsg, $+(%sender, $chr(32), %prefix, $chr(32), %encrypted_data)) + + ; Check if decryption was successful + if (%decrypted != $null && $left(%decrypted, 6) != Error:) { + return %decrypted + } + + ; Decryption failed, return original message + return %message +} + +; === HELPER: Decrypt channel message === +; Attempts to decrypt an encrypted channel message +; Returns decrypted message or original message if decryption fails +alias -l fish11_try_decrypt_channel { + var %channel = $1 + var %message = $2- + + ; Check if process_incoming is enabled + var %process_incoming = $dll(%Fish11DllFile, INI_GetBool, process_incoming 1) + if (%process_incoming == 0) return %message + + ; Get the encryption prefix (default: +FiSH) + var %prefix = $dll(%Fish11DllFile, INI_GetString, encryption_prefix +FiSH) + + ; Check if message starts with the encryption prefix + if ($left(%message, $len(%prefix)) != %prefix) return %message + + ; Extract the encrypted data (after prefix + space) + var %encrypted_data = $mid(%message, $calc($len(%prefix) + 2)) + + ; Try FiSH 11 channel decryption + var %decrypted = $dll(%Fish11DllFile, FiSH11_DecryptMsg, $+(%channel, $chr(32), %prefix, $chr(32), %encrypted_data)) + + ; Check if decryption was successful (no error message) + if (%decrypted != $null && $left(%decrypted, 6) != Error:) { + return %decrypted + } + + ; Decryption failed, return original message + return %message +} + + +; === INCOMING PRIVATE MESSAGES === +on *:TEXT:*:?:{ + ; Try to decrypt the message + var %decrypted = $fish11_try_decrypt_incoming($nick, $1-) + + ; If decryption changed the message, display it and halt + if (%decrypted != $1-) { + ; Check if we should display the decrypted message + var %show_decrypted = $dll(%Fish11DllFile, INI_GetBool, show_decrypted_messages 1) + if (%show_decrypted == 1) { + echo $color(Message text) -dm $nick *** Decrypted: %decrypted + } + ; Let the inject DLL or normal processing handle the rest + } +} + + +; === INCOMING CHANNEL MESSAGES === +on *:TEXT:*:#:{ + ; Try to decrypt the message + var %decrypted = $fish11_try_decrypt_channel($chan, $1-) + + ; If decryption changed the message, display it and halt + if (%decrypted != $1-) { + ; Check if we should display the decrypted message + var %show_decrypted = $dll(%Fish11DllFile, INI_GetBool, show_decrypted_messages 1) + if (%show_decrypted == 1) { + echo $color(Message text) -dm $chan *** Decrypted: %decrypted + } + ; Let the inject DLL or normal processing handle the rest + } +} + + +; === INCOMING PRIVATE NOTICES === +on *:NOTICE:*:?:{ + ; Try to decrypt the message + var %decrypted = $fish11_try_decrypt_incoming($nick, $1-) + + ; If decryption changed the message, display it and halt + if (%decrypted != $1-) { + ; Check if we should display the decrypted message + var %show_decrypted = $dll(%Fish11DllFile, INI_GetBool, show_decrypted_messages 1) + if (%show_decrypted == 1) { + echo $color(Mode text) -dm $nick *** Decrypted notice: %decrypted + } + ; Let the inject DLL or normal processing handle the rest + } +} + + +; === INCOMING PRIVATE ACTIONS === +on *:ACTION:*:?:{ + ; Try to decrypt the message + var %decrypted = $fish11_try_decrypt_incoming($nick, $1-) + + ; If decryption changed the message, display it and halt + if (%decrypted != $1-) { + ; Check if we should display the decrypted message + var %show_decrypted = $dll(%Fish11DllFile, INI_GetBool, show_decrypted_messages 1) + if (%show_decrypted == 1) { + echo $color(Action text) -dm $nick *** Decrypted action: %decrypted + } + ; Let the inject DLL or normal processing handle the rest + } +} + + +; === INCOMING CHANNEL ACTIONS === +on *:ACTION:*:#:{ + ; Try to decrypt the message + var %decrypted = $fish11_try_decrypt_channel($chan, $1-) + + ; If decryption changed the message, display it and halt + if (%decrypted != $1-) { + ; Check if we should display the decrypted message + var %show_decrypted = $dll(%Fish11DllFile, INI_GetBool, show_decrypted_messages 1) + if (%show_decrypted == 1) { + echo $color(Action text) -dm $chan *** Decrypted action: %decrypted + } + ; Let the inject DLL or normal processing handle the rest + } +} + + +; === INCOMING CHANNEL NOTICES === +on *:NOTICE:*:#:{ + ; Try to decrypt the message + var %decrypted = $fish11_try_decrypt_channel($chan, $1-) + + ; If decryption changed the message, display it and halt + if (%decrypted != $1-) { + ; Check if we should display the decrypted message + var %show_decrypted = $dll(%Fish11DllFile, INI_GetBool, show_decrypted_messages 1) + if (%show_decrypted == 1) { + echo $color(Mode text) -dm $chan *** Decrypted notice: %decrypted + } + ; Let the inject DLL or normal processing handle the rest + } +} + + +; === MANUAL DECRYPTION COMMANDS === +; Allow users to manually decrypt a message +; Usage: /fish11_decrypt_msg +alias fish11_decrypt_msg { + if ($1 == $null || $2- == $null) { + echo 4 -a Syntax: /fish11_decrypt_msg + return + } + + var %sender = $1 + var %message = $2- + + var %decrypted = $fish11_try_decrypt_incoming(%sender, %message) + + if (%decrypted != %message) { + echo $color(Mode text) -at *** FiSH_11 Decrypted: %decrypted + } + else { + echo $color(Error) -at *** FiSH_11: failed to decrypt message from %sender + } +} + +; Short alias for manual decryption +alias fdec { fish11_decrypt_msg $1- } diff --git a/scripts/mirc/modules/fish11_encrypt_out.mrc b/scripts/mirc/modules/fish11_encrypt_out.mrc new file mode 100644 index 0000000..7cb5f8f --- /dev/null +++ b/scripts/mirc/modules/fish11_encrypt_out.mrc @@ -0,0 +1,163 @@ +;******************************* +;* FiSH_11 Outgoing Encryption * +;******************************* +; Message encryption for outgoing messages +; Written by GuY, 2025. Licensed under GPL-v3. + +; === OUTGOING MESSAGE HANDLING === +on *:INPUT:*: { + ; Check if message should be processed + var %process_outgoing = $dll(%Fish11DllFile, INI_GetBool, process_outgoing 1) + if (%process_outgoing == 0) return + + ; Get plain prefix + var %plain_prefix = $dll(%Fish11DllFile, INI_GetString, plain_prefix +p) + + ; Don't process if message starts with plain prefix + if ($left($1-, $len(%plain_prefix)) == %plain_prefix) { + return + } + + ; Don't process commands + if (($left($1, 1) == /) && ($1 != /me) && ($1 != /msg) && ($1 != /notice)) return + + ; Handle message too long + if ($len($1-) > 850) { + echo 4 -a Mirc cannot handle lines longer than 850 characters. Text not sent. + haltdef + halt + return + } + + ; Handle message types + var %target = $active + var %message = $1- + var %encrypted = $null + + ; Extract target for /msg and /notice + if ($1 == /msg || $1 == /notice) { + %target = $2 + %message = $3- + } + ; Handle /me actions + else if ($1 == /me) { + ; Check if actions should be encrypted + var %encrypt_action = $dll(%Fish11DllFile, INI_GetBool, encrypt_action 0) + if (%encrypt_action == 0) return + + %message = $2- + } + + ; Determine which encryption system to use + ; Check for: FCEP-1 channel key, manual channel key, legacy key, or private key + + var %encrypted = $null + + ; Check if target is a channel with FCEP-1 or manual key + if ($left(%target, 1) == # || $left(%target, 1) == &) { + ; Channel target - try FiSH 11 encryption (handles both manual and FCEP-1 keys) + %encrypted = $dll(%Fish11DllFile, FiSH11_EncryptMsg, %target %message) + } + else { + ; Private message - check for legacy key first + var %has_legacy_key = $dll(%Fish11DllFile, FiSH10_HasKey, %target) + + if (%has_legacy_key == 1) { + ; Use FiSH 10 legacy encryption (Blowfish) + %encrypted = $dll(%Fish11DllFile, FiSH10_EncryptMsg, %target %message) + } + else { + ; Use FiSH 11 encryption (ChaCha20-Poly1305) + %encrypted = $dll(%Fish11DllFile, FiSH11_EncryptMsg, %target %message) + } + } + + ; Only process if encryption was successful + ; Check for various error indicators: "Error", "no encryption", empty result, or legacy errors + if (%encrypted != $null && $left(%encrypted, 5) != Error && $left(%encrypted, 13) != no encryption && $left(%encrypted, 6) != Legacy) { + ; Add encryption mark if configured + if (%mark_outgoing == [On]) { + if (%mark_style == 1) { + ; Suffix style + echo $color(own text) -t $active < $+ $me $+ > %message $+ $chr(183) + } + else if (%mark_style == 2) { + ; Prefix style + echo $color(own text) -t $active $chr(183) $+ < $+ $me $+ > %message + } + else if (%mark_style == 3) { + ; Colored brackets style + echo $color(own text) -t $active $chr(91) $+ $chr(43) $+ $chr(93) < $+ $me $+ > %message + } + } + else { + ; Display message without encryption mark + echo $color(own text) -t $active < $+ $me $+ > %message + } + + ; Send encrypted message based on command type + if ($1 == /notice) { + .notice %target %encrypted + haltdef + } + else if ($1 == /msg) { + .msg %target %encrypted + haltdef + } + else if ($1 == /me) { + .action %encrypted + haltdef + } + else { + .msg %target %encrypted + haltdef + } + } + else { + ; Encryption failed - display error and prevent sending to server + if (%encrypted != $null) { + echo $color(Error) -at *** FiSH ERROR: %encrypted + } + else { + echo $color(Error) -at *** FiSH ERROR: Encryption failed (no key available) + } + haltdef + halt + } +} + + +; === ENCRYPT MESSAGE === +alias fish11_encrypt { + if (!$1 || !$2) return + ; FiSH11_EncryptMsg expects one data string: " " + var %encrypted = $dll(%Fish11DllFile, FiSH11_EncryptMsg, $1 $2-) + return %encrypted +} + + +; === DECRYPT MESSAGE === +alias fish11_decrypt { + if ($1 == /query) var %cur_contact = $active + else var %cur_contact = $1 + if ($2- == $null) return + + ; FiSH11_DecryptMsg expects one data string: " " + var %decrypted = $dll(%Fish11DllFile, FiSH11_DecryptMsg, $+(%cur_contact,$chr(32),$2-)) + if (%decrypted != $null && $left(%decrypted, 6) != Error:) { + return %decrypted + } + echo $color(Mode text) -at *** FiSH: decryption failed for %cur_contact + return $null +} + + +; === TEST ENCRYPTION === +alias fish11_test_crypt { + if ($1 == $null) var %msg = Test message for encryption + else var %msg = $1- + + echo -s *** FiSH_11 :: TestCrypt -> call DLL with $qt(%msg) + .dll %Fish11DllFile FiSH11_TestCrypt %msg + echo -s *** FiSH_11 :: TestCrypt -> DLL returned +} diff --git a/scripts/mirc/modules/fish11_key_exchange.mrc b/scripts/mirc/modules/fish11_key_exchange.mrc new file mode 100644 index 0000000..79bc4cd --- /dev/null +++ b/scripts/mirc/modules/fish11_key_exchange.mrc @@ -0,0 +1,133 @@ +;******************************* +;* FiSH_11 Key Exchange * +;******************************* +; X25519 key exchange protocol handlers +; Written by GuY, 2025. Licensed under GPL-v3. + +; === AUTO KEY EXCHANGE === +on *:OPEN:?:{ + ; Don't auto-exchange if autokeyx is not enabled + if (%autokeyx != [On]) return + + ; Don't auto-exchange if a legacy DH1080 exchange is in progress + if ($hget(fish10.dh, $nick)) return + + ; Don't auto-exchange if a FiSH 11 exchange is in progress + if ($hget(fish11.dh, $nick)) return + + var %tmp1 = $dll(%Fish11DllFile, FiSH11_FileGetKey, $nick) + + ; Check for error messages or empty result + ; Only initiate exchange if truly no key exists + if (%tmp1 == $null || $left(%tmp1, 2) == no || $left(%tmp1, 5) == Error) { + ; No FiSH 11 key found - check for legacy key + var %has_legacy = $dll(%Fish11DllFile, FiSH10_HasKey, $nick) + if (%has_legacy != 1) { + ; No key at all (neither FiSH 11 nor legacy), initiate FiSH 11 exchange + fish11_X25519_INIT $nick + } + unset %has_legacy + } + unset %tmp1 +} + + +; === KEY EXCHANGE PROTOCOL HANDLERS === +on ^*:NOTICE:X25519_INIT*:?:{ + ; This event triggers when someone initiates a key exchange with us. + ; $1 = X25519_INIT, $2- = public key token from peer + var %their_pub = $2- + + ; Validate incoming key format using regex for robustness. + if (!$regex(%their_pub, /^X25519_INIT:[A-Za-z0-9+\/]{43}=$/)) { + echo $color(Mode text) -tm $nick *** FiSH_11: received invalid INIT key format from $nick + halt + } + + query $nick + echo $color(Mode text) -tm $nick *** FiSH_11: received X25519 public key from $nick, responding... + + ; 1. Generate our own keypair (or get existing one). The DLL returns our public key token. + var %our_pub = $dll(%Fish11DllFile, FiSH11_ExchangeKey, $nick) + + ; 2. Process their public key. This computes and saves the shared secret. + var %process_result = $dll(%Fish11DllFile, FiSH11_ProcessPublicKey, $nick %their_pub) + + ; Check if processing was successful (no error message) + if (%process_result && $left(%process_result, 6) != Error:) { + ; 3. If successful, send our public key back to them so they can complete the exchange. + ; Use more flexible regex to validate public key format + if ($regex(%our_pub, /^X25519_INIT:[A-Za-z0-9+\/=]+$/)) { + .notice $nick X25519_FINISH %our_pub + echo $color(Mode text) -tm $nick *** FiSH_11: sent X25519_FINISH to $nick + } + else { + echo $color(Mode text) -tm $nick *** FiSH_11: ERROR - could not generate our own public key to send in reply. DLL returned: $qt(%our_pub) + } + } + else { + ; Display error message from DLL + echo $color(Mode text) -tm $nick *** FiSH_11: %process_result + } + + halt +} + +on ^*:NOTICE:X25519_FINISH*:?:{ + ; This event triggers when a peer responds to our key exchange initiation. + ; $1 = X25519_FINISH, $2- = public key token from peer + ; Ensure an exchange is in progress with this user by checking the hash table. + if ($hget(fish11.dh, $nick).item != 1) { + echo -at *** FiSH_11: received a FINISH notice, but no key exchange was in progress with $nick $+ . + halt + } + + var %their_pub = $2- + + ; Use regex to validate the key format from the peer. + if ($regex(%their_pub, /^X25519_INIT:[A-Za-z0-9+\/]{43}(=|==)?$/)) { + ; Process the received public key. The DLL computes and stores the shared secret. + var %process_result = $dll(%Fish11DllFile, FiSH11_ProcessPublicKey, $nick %their_pub) + + ; Check if processing was successful (no error message) + if (%process_result && $left(%process_result, 6) != Error:) { + ; Success! Clean up state variables. + hdel fish11.dh $nick + + echo $color(Mode text) -tm $nick *** FiSH_11: key exchange complete with $nick + echo $color(Error) -tm $nick *** FiSH_11 WARNING: key exchange complete, but the identity of $nick is NOT VERIFIED. + echo $color(Error) -tm $nick *** FiSH_11: use /fish_fp11 $nick to see their key fingerprint and verify it with them through a secure channel. + } + else { + ; Display error message from DLL + echo $color(Mode text) -tm $nick *** FiSH_11: %process_result + } + } + else { + echo $color(Mode text) -tm $nick *** FiSH_11: received invalid FINISH key format from $nick $+ : $qt(%their_pub) + } + + halt +} + + +; === KEY EXCHANGE TIMEOUT === +; Handle key exchange timeout +alias fish11_timeout_keyexchange { + if ($1 == $null) { + echo $color(Mode text) -at *** FiSH_11: timeout handler called with no parameters + return + } + + var %contact = $1 + + ; Check if key exchange is still in progress. + if ($hget(fish11.dh, %contact) == 1) { + ; Clean up variables. + hdel fish11.dh %contact + + ; Notify user of timeout with instructions. + echo $color(Mode text) -at *** FiSH_11: key exchange with %contact timed out after $KEY_EXCHANGE_TIMEOUT_SECONDS seconds + echo $color(Mode text) -at *** FiSH_11: to try again, use: /fish11_X25519_INIT %contact + } +} diff --git a/scripts/mirc/modules/fish11_key_management.mrc b/scripts/mirc/modules/fish11_key_management.mrc new file mode 100644 index 0000000..d5d401c --- /dev/null +++ b/scripts/mirc/modules/fish11_key_management.mrc @@ -0,0 +1,455 @@ +;******************************* +;* FiSH_11 Key Management * +;******************************* +; Key set/get/del/list and fingerprint operations +; Written by GuY, 2025. Licensed under GPL-v3. + +; === TRACK NICK CHANGES FOR KEY MANAGEMENT === +on *:NICK:{ + if (($nick == $me) || ($upper($newnick) == $upper($nick))) { return } + if (($query($newnick) == $null) || (%NickTrack != [On])) { return } + + var %old_nick_key = $dll(%Fish11DllFile, FiSH11_FileGetKey, $nick) + + ; If we have a key for the old nick + if ($len(%old_nick_key) > 4) { + var %new_nick_key = $dll(%Fish11DllFile, FiSH11_FileGetKey, $newnick) + + ; If a key already exists for the new nick, warn user about conflict + if ($len(%new_nick_key) > 4) { + echo $color(Error) -at *** FiSH_11: nick change conflict ! You have a key for $nick, who is now $newnick. However, you ALREADY have a different key for $newnick. No keys were changed. Please resolve this manually. + unset %old_nick_key + unset %new_nick_key + return + } + + ; Store the key under the new nickname + if ($dll(%Fish11DllFile, FiSH11_SetKey, $+($network," ",$newnick," ",%old_nick_key))) { + echo $color(Mode text) -at *** FiSH_11: key for $nick has been moved to new nick $newnick. + ; Remove the key from the old nickname to prevent reuse by another user + noop $dll(%Fish11DllFile, FiSH11_FileDelKey, $+($network," ",$nick)) + } + unset %new_nick_key + } + unset %old_nick_key +} + + +; === KEY MANAGEMENT FUNCTIONS === +; Set key with different encoding options +alias fish11_setkey { + if ($1 == $null || $2 == $null) { + echo 4 -a Syntax: /fish11_setkey + return + } + ; $1 = nickname (data), $2- = key (parms) + var %msg = $dll(%Fish11DllFile, FiSH11_SetKey, $+($network, $chr(32), $1, $chr(32), $2-)) + if (%msg && $left(%msg, 6) != Error:) { + echo -a *** FiSH_11: key set for $1 on network $network + } + else { + var %error_msg = $iif(%msg, %msg, "Unknown error - could not set key for $1") + echo -a *** FiSH_11: error setting key for $1 - %error_msg + } + unset %msg +} + +alias fish11_setkey_manual { + if ($1 == $null || $2 == $null) { + echo 4 -a Syntax: /fish11_setkey_manual <#channel> + echo 4 -a Example: /fish11_setkey_manual #secret AGN2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9T0 + return + } + + ; Validate channel name format + var %channel = $1 + if (!$regex(%channel, /^[#&]/)) { + echo 4 -a Error: Channel name must start with # or & + return + } + + ; Validate key format (base64, 44 chars) + var %key = $2- + + if ($len(%key) == 44 && $regex(%key, /^[A-Za-z0-9+\/=]+$/)) { + ; Valid base64 key - use standard function + var %input = $+(%channel, $chr(32), %key) + var %msg = $dll(%Fish11DllFile, FiSH11_SetManualChannelKey, %input) + } + else { + ; Non-base64 or different length - use password derivation function + var %input = $+(%channel, $chr(32), %key) + var %msg = $dll(%Fish11DllFile, FiSH11_SetManualChannelKeyFromPassword, %input) + } + + if (%msg && $left(%msg, 6) != Error:) { + echo -a *** FiSH_11: manual channel key set for %channel + } + else { + var %error_msg = $iif(%msg, %msg, "Unknown error - could not set manual key for %channel") + echo -a *** FiSH_11: error setting manual key for %channel - %error_msg + } +} + +alias fish11_setkey_utf8 { fish11_writekey raw_bytes $1 $2- } + +; Helper for setting keys +alias fish11_writekey { + if ($2 == /query) var %cur_contact = $active + else var %cur_contact = $2 + if ($3- == $null) return + + ; Comprehensive network sanitization - remove all special characters + var %network = $regsubex($network, /[^\w\d]/g, _) + + if ($dll(%Fish11DllFile, FiSH11_SetKey, $+(%network," ",%cur_contact," ",$3-))) { + var %info = *** FiSH_11: key for %cur_contact set to *censored* + + if ($window(%cur_contact) == $null) echo $color(Mode text) -at %info + else echo $color(Mode text) -tm %cur_contact %info + return $true + } + return $false +} + + +; === KEY EXCHANGE INIT === +; Initialize key exchange +alias fish11_X25519_INIT { + if (($1 == /query) || ($1 == $null)) var %cur_contact = $active + else var %cur_contact = $1 + + ; If there's an existing exchange in progress, cancel it first + if ($hget(fish11.dh, %cur_contact) == 1) { + echo $color(Mode text) -at *** FiSH_11: restarting key exchange with %cur_contact + } + + ; Use a hash table to track in-progress exchanges. + hadd -m fish11.dh %cur_contact 1 + + var %pub = $dll(%Fish11DllFile, FiSH11_ExchangeKey, %cur_contact) + + ; Use regex to validate the entire key format. + if ($regex(%pub, /^X25519_INIT:[A-Za-z0-9+\/=]+$/)) { + .notice %cur_contact X25519_INIT %pub + echo $color(Mode text) -tm %cur_contact *** FiSH_11: sent X25519_INIT to %cur_contact $+ , waiting for reply... + } + else { + ; Fallback: show what we got (safely) + echo $color(Mode text) -at *** FiSH_11: ERROR - key exchange initiation failed. DLL returned: $qt(%pub) + } + + ; Start a timer to cancel the exchange if no response is received + .timer.fish11_x25519_ $+ %cur_contact 1 %KEY_EXCHANGE_TIMEOUT_SECONDS fish11_timeout_keyexchange %cur_contact +} + +; Process received public key +alias fish11_ProcessPublicKey { + if ($1 == $null || $2 == $null) { + echo 4 -a Syntax: /fish11_ProcessPublicKey + return + } + + ; Process the public key + var %result = $dll(%Fish11DllFile, FiSH11_ProcessPublicKey, $1 $2-) + + ; Check if processing was successful (no error message) + if (%result && $left(%result, 6) != Error:) { + echo $color(Mode text) -at *** FiSH_11: key exchange completed with $1 + echo $color(Error) -at *** FiSH_11 WARNING: key exchange complete, but the identity of $1 is NOT VERIFIED. + echo $color(Error) -at *** FiSH_11: use /fish_fp11 $1 to see their key fingerprint and verify it with them through a secure channel (e.g., voice call). + } + else { + ; Display error message from DLL + echo $color(Mode text) -at *** FiSH_11: %result + } +} + +; Shorthand for key exchange +alias keyx { fish11_X25519_INIT $1 } + + +; === USE KEY FROM ANOTHER CHANNEL/USER === +alias fish11_usechankey { + if ($server == $null) { + echo $color(Mode text) -at *** FiSH_11: ERROR - not connected to a server. + return + } + var %theKey = $dll(%Fish11DllFile, FiSH11_FileGetKey, $2) + if (%theKey == $null) { + echo $color(Mode text) -at *** FiSH_11: no valid key for $2 found + } + else { + if ($dll(%Fish11DllFile, FiSH11_SetKey, $+($network," ",$1," ",%theKey))) { + echo $color(Mode text) -at *** FiSH_11: using same key as $2 for $1 + } + unset %theKey + } +} + + +; === SHOW KEY === +alias fish11_showkey { + if ($1 == /query) var %cur_contact = $active + else var %cur_contact = $1 + + var %theKey = $dll(%Fish11DllFile, FiSH11_FileGetKey, %cur_contact) + if (%theKey != $null) { + window -dCo +l @FiSH-Key -1 -1 500 120 + aline @FiSH-Key Key for %cur_contact : + aline -p @FiSH-Key %theKey + + ; Show key TTL (expiration) if available + var %ttl = $dll(%Fish11DllFile, FiSH11_GetKeyTTL, %cur_contact) + if (%ttl == EXPIRED) { + aline @FiSH-Key Status: EXPIRED - use /fish11_X25519_INIT %cur_contact to renew + } + else if (%ttl == NO_TTL) { + aline @FiSH-Key Status: No expiration (manually set key) + } + else if (%ttl isnum) { + var %hours = $int($calc(%ttl / 3600)) + var %mins = $int($calc((%ttl % 3600) / 60)) + aline @FiSH-Key Status: Expires in %hours hours %mins minutes + } + + unset %theKey + } + else { + echo $color(Mode text) -at *** FiSH_11: no valid key for %cur_contact found + } +} + + +; === REMOVE KEY === +alias fish11_removekey { + if ($1 == /query) var %cur_contact = $active + else var %cur_contact = $1 + + ; Get result message from DLL + var %msg = $dll(%Fish11DllFile, FiSH11_FileDelKey, $+($network," ",%cur_contact)) + + ; Display message from DLL (works for both success and error) + if (%msg) { + echo $color(Mode text) -at *** FiSH_11: %msg + } + else { + echo $color(Mode text) -at *** FiSH_11: error - could not remove key for %cur_contact + } +} + + +; === SAFETY FUNCTION === +; Safety function to prevent accidental key overwrites +alias fish11_setkey_safe { + var %target = $1 + var %existing_key = $dll(%Fish11DllFile, FiSH11_FileGetKey, %target) + + if ($len(%existing_key) > 1) { + if ($?!="Key already exists for %target $+ . Overwrite? (Yes/No)") { + fish11_setkey %target $2- + } + } + else { + fish11_setkey %target $2- + } +} + + +; === KEY TTL (EXPIRATION) === +; Show the remaining lifetime of an exchange key +; Exchange keys have a 24-hour TTL from creation time +; Usage: /fish11_keyttl +alias fish11_keyttl { + if ($1 == $null) { + echo 4 -a Syntax: /fish11_keyttl + return + } + + var %nickname = $1 + var %ttl = $dll(%Fish11DllFile, FiSH11_GetKeyTTL, %nickname) + + if (%ttl == EXPIRED) { + echo $color(Error) -at *** FiSH_11: key for %nickname has EXPIRED + echo $color(Mode text) -at *** FiSH_11: use /fish11_X25519_INIT %nickname to establish a new key + } + else if (%ttl == NO_TTL) { + echo $color(Mode text) -at *** FiSH_11: key for %nickname has no expiration (manually set key) + } + else if (%ttl isnum) { + var %hours = $int($calc(%ttl / 3600)) + var %mins = $int($calc((%ttl % 3600) / 60)) + echo $color(Mode text) -at *** FiSH_11: key for %nickname expires in %hours hours %mins minutes + } + else { + echo $color(Error) -at *** FiSH_11: could not get key TTL for %nickname + } +} + +; Short alias for key TTL +alias fkeyttl { fish11_keyttl $1 } + + +; === LIST ALL KEYS === +alias fish11_file_list_keys { + ; Check the DLL exists before trying to call it + if (!$isfile(%Fish11DllFile)) { + echo $color(Mode text) -at *** FiSH ERROR- DLL not found: %Fish11DllFile + return + } + ; Log that we're about to call the function + echo $color(Mode text) -at *** FiSH: listing keys... + ; Ensure MIRCDIR is set (should already be set at startup, but be safe) + noop $dll(%Fish11DllFile, FiSH11_SetMircDir, $mircdir) + + ; Call DLL function using proper syntax for data return + echo $color(Mode text) -at *** FiSH: about to call FiSH11_FileListKeys... + var %keys = $dll(%Fish11DllFile, FiSH11_FileListKeys, $null) + echo $color(Mode text) -at *** FiSH: DLL call completed, result length: $len(%keys) + + ; Check for errors (DLL returns "Error: ..." for errors) + if ($left(%keys, 6) == Error:) { + echo $color(Error) -at *** FiSH ERROR: %keys + return + } + + ; If the function returns data, display it line by line + if (%keys != $null && $len(%keys) > 0) { + fish11_display_multiline_result %keys + } + else { + echo $color(Mode text) -at *** FiSH: no keys found + } +} + + +; Helper function to safely display multi-line text from DLL +alias -l fish11_display_multiline_result { + var %text = $1- + var %line_count = 0 + var %max_lines = 100 + + ; Handle different line ending formats + %text = $replace(%text, $chr(13) $+ $chr(10), $chr(1)) + %text = $replace(%text, $chr(13), $chr(1)) + %text = $replace(%text, $chr(10), $chr(1)) + + ; Display each line + var %i = 1 + var %num_tokens = $numtok(%text, 1) + while (%i <= %num_tokens) { + var %line = $gettok(%text, %i, 1) + + ; Safety check: limit number of lines to prevent crashes + inc %line_count + if (%line_count > %max_lines) { + echo $color(Mode text) -at *** FiSH_11: output truncated (exceeded %max_lines lines) + break + } + + ; Display the line with proper formatting + if ($len(%line) > 0) { + echo $color(Mode text) -at %line + } + + inc %i + } +} + + +; === FINGERPRINT === +; Helper function to get and format colored fingerprint for a target +; Returns the colored fingerprint or $null if not available +; Also caches the result in %fish11.lastfingerprint. +alias -l fish11_GetColoredFingerprint { + var %target = $1 + + ; Check if there's a cached fingerprint first + if (%fish11.lastfingerprint. $+ [ %target ] != $null) { + return $($+(%,fish11.lastfingerprint.,%target),2) + } + + ; Get the fingerprint from DLL + var %fingerprint = $dll(%Fish11DllFile, FiSH11_GetKeyFingerprint, %target) + + ; Check if the response is an error message + if ($left(%fingerprint, 6) == Error:) { + return $null + } + + ; Extract just the fingerprint part from the response + var %fp_only = $gettok(%fingerprint, 2-, 58) + var %fp_only = $strip(%fp_only) + + ; Validate that we have a proper fingerprint + if (%fp_only == $null || $len(%fp_only) < 10 || $pos(%fp_only, $chr(32)) == 0) { + return $null + } + + ; Format each group with a different color + var %group1 = $gettok(%fp_only, 1, 32) + var %group2 = $gettok(%fp_only, 2, 32) + var %group3 = $gettok(%fp_only, 3, 32) + var %group4 = $gettok(%fp_only, 4, 32) + + ; Validate that we have at least 4 groups + if (%group1 == $null || %group2 == $null || %group3 == $null || %group4 == $null) { + return $null + } + + ; Create colored version using mIRC color codes + ; 04=red, 12=blue, 03=green, 07=orange + var %colored_fp = 04 $+ %group1 $+ 12 $+ %group2 $+ 03 $+ %group3 $+ 07 $+ %group4 + + ; Cache the result + set %fish11.lastfingerprint. $+ [ %target ] %colored_fp + + return %colored_fp +} + +; Display key fingerprint with color for a target +alias fish11_showfingerprint { + if ($1 == /query) { + var %target = $active + } + else { + var %target = $1 + } + + ; Check if we have a key for this target + var %key = $dll(%Fish11DllFile, FiSH11_FileGetKey, %target) + + if ($len(%key) > 1) { + var %colored_fp = $fish11_GetColoredFingerprint(%target) + + if (%colored_fp != $null) { + ; Display the colored fingerprint + echo $color(Mode text) -at *** FiSH_11: key fingerprint for %target is: %colored_fp + } + else { + echo $color(Mode text) -at *** FiSH_11: Error: could not retrieve valid fingerprint for %target + } + } + else { + echo $color(Mode text) -at *** FiSH_11: no key found for %target + } +} + + +; === ALIAS SHORTCUTS FOR USER COMMANDS === +alias fish_genkey11 { fish11_setkey_safe $1 $2- } +alias fish_setkey11 { fish11_setkey $1 $2- } +alias fish_getkey11 { fish11_showkey $1 } +alias fish_fp11 { fish11_showfingerprint $1- } +alias fish_delkey11 { fish11_removekey $1 } +alias fish_listkeys11 { fish11_file_list_keys } +alias fish_encrypt11 { return $fish11_encrypt($1, $2-) } +alias fish_decrypt11 { return $fish11_decrypt($1, $2-) } +alias fish_keyx11 { fish11_X25519_INIT $1 } +alias fish_keyp11 { fish11_ProcessPublicKey $1 $2- } +alias fish_keyttl11 { fish11_keyttl $1 } +alias fish_test11 { fish11_test_crypt $1- } +alias fish_help11 { fish11_help } +alias fish_version11 { fish11_version } +alias fish_initchannel11 { fish11_initchannel $1- } +alias fish_stats11 { fish11_stats } diff --git a/scripts/mirc/modules/fish11_legacy.mrc b/scripts/mirc/modules/fish11_legacy.mrc new file mode 100644 index 0000000..619e5ee --- /dev/null +++ b/scripts/mirc/modules/fish11_legacy.mrc @@ -0,0 +1,273 @@ +;******************************* +;* FiSH_10 Legacy Compatibility * +;******************************* +; DH1080 key exchange and Blowfish encryption for backward compatibility +; Written by GuY, 2025. Licensed under GPL-v3. + +; === LEGACY FiSH 10 KEY MANAGEMENT === + +alias fish10_setkey { + if ($1 == $null || $2 == $null) { + echo 4 -a Syntax: /fish10_setkey + return + } + var %msg = $dll(%Fish11DllFile, FiSH10_SetKey, $1 $2-) + if (%msg && $left(%msg, 6) != Error:) { + echo -a *** FiSH_10: %msg + } + else { + echo -a *** FiSH_10: error setting key - %msg + } +} + +alias fish10_delkey { + if ($1 == $null) var %target = $active + else var %target = $1 + var %msg = $dll(%Fish11DllFile, FiSH10_DelKey, %target) + if (%msg && $left(%msg, 6) != Error:) { + echo -a *** FiSH_10: %msg + } + else { + echo -a *** FiSH_10: error removing key - %msg + } +} + +alias fish10_showkey { + if ($1 == $null) var %target = $active + else var %target = $1 + var %key = $dll(%Fish11DllFile, FiSH10_GetKey, %target) + if ($left(%key, 6) == Error:) { + echo -a *** FiSH_10: error retrieving key for %target : %key + } + elseif (%key == $null) { + echo -a *** FiSH_10: no key found for %target + } else { + echo -a *** FiSH_10: key for %target : %key + } +} + +alias fish10_usechankey { + if ($1 == $null || $2 == $null) { + echo 4 -a Syntax: /fish10_usechankey + return + } + + var %target = $1 + var %source = $2 + + ; Get the key from the source + var %key = $dll(%Fish11DllFile, FiSH11_FileGetKey, %source) + + if (%key == $null || $len(%key) < 4) { + echo $color(Error) -at *** FiSH_10: no valid key found for %source + return + } + + ; Set the same key for the target + fish10_setkey %target %key + echo $color(Mode text) -at *** FiSH_10: using same key as %source for %target +} + + +; === LEGACY DH1080 KEY EXCHANGE === + +alias fish10_keyx { + if ($1 == $null) var %target = $active + else var %target = $1 + + ; Use hash table to track in-progress exchanges + hadd -m fish10.dh %target 1 + + var %pub = $dll(%Fish11DllFile, FiSH10_DH1080_GenerateKeyPair, %target) + + ; Check if key generation was successful (should be a base64 string ending with 'A') + ; DH1080 public keys are ~181 chars long and end with 'A' + if ($len(%pub) > 100 && $right(%pub, 1) == A) { + .notice %target DH1080_INIT %pub + echo $color(Mode text) -tm %target *** FiSH_10: sent DH1080_INIT to %target $+ , waiting for reply... + + ; Set timeout timer + if (%KEY_EXCHANGE_TIMEOUT_SECONDS == $null) { var %timeout = 10 } + else { var %timeout = %KEY_EXCHANGE_TIMEOUT_SECONDS } + .timer.fish10_dh1080_ $+ %target 1 %timeout fish10_timeout_keyexchange %target + } + else { + hdel fish10.dh %target + echo $color(Error) -at *** FiSH_10: DH1080 init failed - %pub + } +} + +; Handle key exchange timeout for FiSH 10/DH1080 +alias fish10_timeout_keyexchange { + if ($1 == $null) return + var %contact = $1 + + ; Check if key exchange is still in progress + if ($hget(fish10.dh, %contact) == 1) { + hdel fish10.dh %contact + echo $color(Mode text) -at *** FiSH_10: key exchange with %contact timed out + } +} + + +; === LEGACY DH1080 NOTICE HANDLERS === + +on ^*:NOTICE:DH1080_INIT*:?:{ + ; In mIRC NOTICE events: + ; $1 = "DH1080_INIT" + ; $2 = public key (base64) + ; $3 = "CBC" (optional) + var %their_pub = $2 + + ; Validate format: DH1080 public keys should be base64 + if (!$regex(%their_pub, /^[A-Za-z0-9+\/=]+$/)) { + echo $color(Error) -tm $nick *** FiSH_10: received invalid DH1080_INIT format from $nick + halt + } + + echo $color(Mode text) -tm $nick *** FiSH_10: received DH1080_INIT from $nick, responding... + + var %our_pub = $dll(%Fish11DllFile, FiSH10_DH1080_GenerateKeyPair, $nick) + + ; Check if key generation failed + if ($left(%our_pub, 6) == Error:) { + echo $color(Error) -tm $nick *** FiSH_10: key generation failed - %our_pub + halt + } + + var %secret = $dll(%Fish11DllFile, FiSH10_DH1080_ComputeSecret, $nick %their_pub) + + ; Check if secret computation failed + if ($left(%secret, 6) == Error:) { + echo $color(Error) -tm $nick *** FiSH_10: key exchange failed - %secret + halt + } + + ; The DLL automatically stores the shared secret as a Blowfish key for this nick + + ; Send DH1080_FINISH response with our public key + .notice $nick DH1080_FINISH %our_pub + + echo $color(Mode text) -tm $nick *** FiSH_10: key exchange complete with $nick + echo $color(Error) -tm $nick *** FiSH_10 WARNING: key exchange complete, but the identity of $nick is NOT VERIFIED. + halt +} + +on ^*:NOTICE:DH1080_FINISH*:?:{ + ; Verify an exchange is in progress + if ($hget(fish10.dh, $nick) != 1) { + echo -at *** FiSH_10: received DH1080_FINISH but no key exchange was in progress with $nick + halt + } + + ; In mIRC NOTICE events: + ; $1 = "DH1080_FINISH" + ; $2 = public key (base64) + var %their_pub = $2 + + ; Validate format + if (!$regex(%their_pub, /^[A-Za-z0-9+\/=]+$/)) { + echo $color(Error) -tm $nick *** FiSH_10: received invalid DH1080_FINISH format from $nick + hdel fish10.dh $nick + halt + } + + var %secret = $dll(%Fish11DllFile, FiSH10_DH1080_ComputeSecret, $nick %their_pub) + + ; Clean up tracking + hdel fish10.dh $nick + + ; Check if secret computation failed + if ($left(%secret, 6) == Error:) { + echo $color(Error) -tm $nick *** FiSH_10: key exchange failed - %secret + halt + } + + ; The DLL automatically stores the shared secret as a Blowfish key for this nick + + echo $color(Mode text) -tm $nick *** FiSH_10: key exchange complete with $nick + echo $color(Error) -tm $nick *** FiSH_10 WARNING: key exchange complete, but the identity of $nick is NOT VERIFIED. + halt +} + + +; === LEGACY TOPIC MANAGEMENT === + +; Set a plaintext topic for a channel in the legacy fish10 section +alias fish10_settopic { + if ($1 == $null || $2- == $null) { + echo 4 -a Syntax: /fish10_settopic <#channel> + return + } + + var %channel = $1 + var %topic = $2- + + ; Validate channel name + if (!$regex(%channel, /^[#&]/)) { + echo $color(Error) -at *** FiSH_10 ERROR: Invalid channel name %channel (must start with # or &) + return + } + + var %result = $dll(%Fish11DllFile, FiSH10_SetTopic, $+(%channel, $chr(32), %topic)) + + if (%result && $left(%result, 6) != Error:) { + echo $color(Mode text) -at *** FiSH_10: %result + } + else { + var %error_msg = $iif(%result, %result, "Unknown error - could not set topic for %channel") + echo $color(Error) -at *** FiSH_10: error setting topic for %channel - %error_msg + } +} + +; Get a plaintext topic for a channel from the legacy fish10 section +alias fish10_gettopic { + if ($1 == $null) { + echo 4 -a Syntax: /fish10_gettopic <#channel> + return + } + + var %channel = $1 + + ; Validate channel name + if (!$regex(%channel, /^[#&]/)) { + echo $color(Error) -at *** FiSH_10 ERROR: Invalid channel name %channel (must start with # or &) + return + } + + var %result = $dll(%Fish11DllFile, FiSH10_GetTopic, %channel) + + if (%result && $left(%result, 6) != Error:) { + echo $color(Mode text) -at *** FiSH_10: Topic for %channel is: %result + } + else { + var %error_msg = $iif(%result, %result, "Unknown error - could not get topic for %channel") + echo $color(Error) -at *** FiSH_10: error getting topic for %channel - %error_msg + } +} + +; Remove a plaintext topic for a channel from the legacy fish10 section +alias fish10_removetopic { + if ($1 == $null) { + echo 4 -a Syntax: /fish10_removetopic <#channel> + return + } + + var %channel = $1 + + ; Validate channel name + if (!$regex(%channel, /^[#&]/)) { + echo $color(Error) -at *** FiSH_10 ERROR: Invalid channel name %channel (must start with # or &) + return + } + + var %result = $dll(%Fish11DllFile, FiSH10_RemoveTopic, %channel) + + if (%result && $left(%result, 6) != Error:) { + echo $color(Mode text) -at *** FiSH_10: %result + } + else { + var %error_msg = $iif(%result, %result, "Unknown error - could not remove topic for %channel") + echo $color(Error) -at *** FiSH_10: error removing topic for %channel - %error_msg + } +} diff --git a/scripts/mirc/modules/fish11_masterkey.mrc b/scripts/mirc/modules/fish11_masterkey.mrc new file mode 100644 index 0000000..d2d9d35 --- /dev/null +++ b/scripts/mirc/modules/fish11_masterkey.mrc @@ -0,0 +1,111 @@ +;******************************* +;* FiSH_11 Master Key * +;******************************* +; Master key management for encrypting configuration +; Written by GuY, 2025. Licensed under GPL-v3. + +; === MASTER KEY MANAGEMENT === + +; Check if master key is unlocked, prompt if not +alias fish11_check_masterkey { + var %is_unlocked = $dll(%Fish11DllFile, FiSH11_MasterKeyIsUnlocked, $null) + + if (%is_unlocked != yes) { + ; Prompt user for master key password + echo $color(Mode text) -at *** FiSH_11: master key is locked. Configuration and logs are NOT encrypted. + echo $color(Mode text) -at *** FiSH_11: use /fish11_unlock to unlock the master key. + } + else { + echo $color(Mode text) -at *** FiSH_11: master key is unlocked. Configuration and logs ARE encrypted. + } +} + +; Unlock master key with password +; Usage: /fish11_unlock [password] +; If no password provided, prompts with $input dialog +alias fish11_unlock { + var %password = $1- + + ; If no password provided, prompt with dialog + if (%password == $null) { + %password = $input(Enter master key password:, pvq, FiSH_11 Master Key, ) + } + + ; If user cancelled or empty password + if (%password == $null) { + echo $color(Error) -at *** FiSH_11: master key unlock cancelled. + return + } + + ; Call DLL to unlock + var %result = $dll(%Fish11DllFile, FiSH11_MasterKeyUnlock, %password) + + ; Clear password from memory + unset %password + + ; Display result + if (%result) { + echo $color(Mode text) -at *** FiSH_11: %result + } + else { + echo $color(Error) -at *** FiSH_11: failed to unlock master key + } +} + +; Lock master key (clear from memory) +; Usage: /fish11_lock +alias fish11_lock { + var %result = $dll(%Fish11DllFile, FiSH11_MasterKeyLock, $null) + + if (%result) { + echo $color(Mode text) -at *** FiSH_11: %result + } + else { + echo $color(Error) -at *** FiSH_11: Failed to lock master key + } +} + +; Show master key status +; Usage: /fish11_masterkey_status +alias fish11_masterkey_status { + var %result = $dll(%Fish11DllFile, FiSH11_MasterKeyStatus, $null) + + if (%result) { + echo $color(Mode text) -at *** FiSH_11: %result + } + else { + echo $color(Error) -at *** FiSH_11: Failed to get master key status + } +} + +; Require master key password (loop until unlocked) +; Similar to mircryption's mc_requirepassphrase +alias fish11_require_masterkey { + var %is_unlocked = $dll(%Fish11DllFile, FiSH11_MasterKeyIsUnlocked, $null) + + while (%is_unlocked != yes) { + var %password = $input(Master key is locked. Enter password to unlock :, pvq, FiSH_11 Master Key Required, ) + + ; If user cancelled + if (%password == $null) { + echo $color(Error) -at *** FiSH_11: master key unlock is required. Cancelling operation. + return + } + + ; Try to unlock + var %result = $dll(%Fish11DllFile, FiSH11_MasterKeyUnlock, %password) + unset %password + + ; Check if now unlocked + %is_unlocked = $dll(%Fish11DllFile, FiSH11_MasterKeyIsUnlocked, $null) + + if (%is_unlocked == yes) { + echo $color(Mode text) -at *** FiSH_11: master key unlocked successfully + } + else { + echo $color(Error) -at *** FiSH_11: incorrect password. Try again. + } + } +} + +; === END MASTER KEY MANAGEMENT === diff --git a/scripts/mirc/modules/fish11_menus.mrc b/scripts/mirc/modules/fish11_menus.mrc new file mode 100644 index 0000000..baf4229 --- /dev/null +++ b/scripts/mirc/modules/fish11_menus.mrc @@ -0,0 +1,332 @@ +;******************************* +;* FiSH_11 Menus * +;******************************* +; All mIRC menus for FiSH_11 +; Written by GuY, 2025. Licensed under GPL-v3. + +; === MENUS === + +; Menu for channel windows +menu channel { + - + FiSH_11 channel encryption + .Add a channel key encryption + ..Manual key : fish11_set_manual_key_dialog $chan + ..FCEP-1 key : fish11_init_fcep_dialog $chan + .Encrypt topic + ..Enable topic encryption :{ fish11_SetChannelIniValue $chan encrypt_topic 1 | echo $color(Mode text) -at *** FiSH: topic encryption enabled for $chan } + ..Disable topic encryption :{ fish11_SetChannelIniValue $chan encrypt_topic 0 | echo $color(Mode text) -at *** FiSH: topic encryption disabled for $chan } + .- + .Show channel key info : fish11_show_channel_key_info $chan + .Remove channel key : fish11_remove_channel_key $chan + .- + .Show key :fish11_showkey $chan + .Show fingerprint :fish11_showfingerprint $chan + .Copy fingerprint to clipboard :{ + fish11_showfingerprint $chan + var %fp = %fish11.lastfingerprint. $+ [ $chan ] + if (%fp != $null) { + clipboard %fp + echo $color(Mode text) -at *** FiSH_11: fingerprint for $chan copied to clipboard + } + } + .- + .Encrypt message :{ + var %msg = $?="Enter message to encrypt:" + if (%msg) { + var %encrypted = $fish11_encrypt($chan,%msg) + echo $color(Mode text) -at *** FiSH: encrypted message: %encrypted + } + } + .Decrypt message :{ + var %msg = $?="Enter message to decrypt:" + if (%msg) { + var %decrypted = $fish11_decrypt($chan,%msg) + echo $color(Mode text) -at *** FiSH: decrypted message: %decrypted + } + } + .Set topic (encrypted) :{ + var %topic = $?="Enter encrypted topic for " $+ $chan $+ ":" + if (%topic != $null) etopic %topic + } + .Set topic (plaintext) :{ + var %topic = $?="Enter plaintext topic for " $+ $chan $+ ":" + if (%topic != $null) settopic $chan %topic + } + .Get topic (plaintext) :{ + var %result = $gettopic($chan) + if (%result != $null) { + echo $color(Mode text) -at *** FiSH_11: Topic for $chan is: %result + } + } + - + FiSH_10 legacy (Blowfish) + .Show legacy key :fish10_showkey $chan + .Set legacy key... :{ var %key = $?="Enter hex Blowfish key (4-56 bytes):" | if (%key != $null) fish10_setkey $chan %key } + .Remove legacy key :fish10_delkey $chan +} + +; Menu for query windows +menu query { + - + FiSH_11 + .X25519 keyXchange: fish11_X25519_INIT $1 + .- + .Show key :fish11_showkey $1 + .Show fingerprint :fish11_showfingerprint $1 + .Copy fingerprint to clipboard :{ + fish11_showfingerprint $1 + var %fp = %fish11.lastfingerprint. $+ [ $1 ] + if (%fp != $null) { + clipboard %fp + echo $color(Mode text) -at *** FiSH_11: fingerprint for $1 copied to clipboard + } + } + .- + .Set manual key... :{ var %key = $?="Enter manual key for " $+ $1 $+ ":" | if (%key != $null) fish11_setkey_manual $1 %key } + .Set new key :{ var %key = $?="Enter new key for " $+ $1 $+ ":" | if (%key != $null) fish11_setkey $1 %key } + .Set new key (UTF-8) :{ var %key = $?="Enter new key for " $+ $1 $+ " (UTF-8):" | if (%key != $null) fish11_setkey_utf8 $1 %key } + .Remove key :fish11_removekey $1 + .- + .Encrypt message :{ + var %msg = $?="Enter message to encrypt:" + if (%msg) { + var %encrypted = $fish11_encrypt($1,%msg) + echo $color(Mode text) -at *** FiSH: encrypted message: %encrypted + } + } + .Decrypt message :{ + var %msg = $?="Enter message to decrypt:" + if (%msg) { + var %decrypted = $fish11_decrypt($1,%msg) + echo $color(Mode text) -at *** FiSH: decrypted message: %decrypted + } + } + - + FiSH_10 legacy (DH1080) + .DH1080 keyXchange: fish10_keyx $1 + .- + .Show legacy key :fish10_showkey $1 + .Set legacy key... :{ var %key = $?="Enter hex Blowfish key (4-56 bytes) for " $+ $1 $+ ":" | if (%key != $null) fish10_setkey $1 %key } + .Remove legacy key :fish10_delkey $1 +} + +; Menu for nicklist +menu nicklist { + - + FiSH_11 + .X25519 keyXchange: fish11_X25519_INIT $1 + .- + .Show key :fish11_showkey $1 + .Show fingerprint :fish11_showfingerprint $1 + .- + .Set manual key... :{ var %key = $?="Enter manual key for " $+ $1 $+ ":" | if (%key != $null) fish11_setkey_manual $1 %key } + .Set new key :{ var %key = $?="Enter new key for " $+ $1 $+ ":" | if (%key != $null) fish11_setkey $1 %key } + .Set new key (UTF-8) :{ var %key = $?="Enter new key for " $+ $1 $+ " (UTF-8):" | if (%key != $null) fish11_setkey_utf8 $1 %key } + .Remove key :fish11_removekey $1 + .Use same key as $chan :fish11_usechankey $1 $chan + .- + .Encrypt message :{ + var %msg = $?="Enter message to encrypt:" + if (%msg) { + var %encrypted = $fish11_encrypt($1,%msg) + echo $color(Mode text) -at *** FiSH: encrypted message: %encrypted + } + } + .Decrypt message :{ + var %msg = $?="Enter message to decrypt:" + if (%msg) { + var %decrypted = $fish11_decrypt($1,%msg) + echo $color(Mode text) -at *** FiSH: decrypted message: %decrypted + } + } + - + FiSH_10 legacy (DH1080) + .DH1080 keyXchange: fish10_keyx $1 + .- + .Show legacy key :fish10_showkey $1 + .Set legacy key... :{ var %key = $?="Enter hex Blowfish key (4-56 bytes) for " $+ $1 $+ ":" | if (%key != $null) fish10_setkey $1 %key } + .Remove legacy key :fish10_delkey $1 + .Use same legacy key as $chan :fish10_usechankey $1 $chan +} + +; Common menu available in all windows +menu status,channel,nicklist,query { + FiSH_11 + .Core version :fish11_version + .Injection version : fish11_injection_version + .Help :fish11_help + .- + .Master key + ..Unlock master key :fish11_unlock + ..Lock master key :fish11_lock + ..Show master key status :fish11_masterkey_status + .- + .Set topic (encrypted) :{ + ; Only allow in channel windows + if ($window($active).type != channel) { + echo $color(Mode text) -at *** FiSH_11: etopic can only be used in channel windows + return + } + var %topic = $?="Enter encrypted topic for " $+ $active $+ ":" + if (%topic != $null) etopic %topic + } + .Add channel key encryption :{ + ; Only allow in channel windows (more robust check) + if ($window($active).type != channel) { + echo $color(Mode text) -at *** FiSH_11: This command can only be used in channel windows + return + } + ; Open a dialog to choose encryption method + var %choice = $input(Add Channel Key Encryption for $active $+ :, pvq, FiSH_11 Add Channel Key) + if (%choice == $null) return + + if (%choice == 1) { + ; Set Manual Key + fish11_set_manual_key_dialog $active + } + elseif (%choice == 2) { + ; Set FCEP-1 Key + fish11_init_fcep_dialog $active + } + } + .List all keys :fish11_file_list_keys + .Test encryption :fish11_test_crypt + .- + .Set plain-prefix :{ var %prefix = $?="Enter new plain-prefix:" | if (%prefix != $null) fish11_prefix %prefix } + .Auto-KeyXchange $+ $chr(32) $+ %autokeyx + ..Enable :set %autokeyx [On] + ..Disable :set %autokeyx [Off] + .Misc config + ..Encrypt outgoing [Status] + ...Enable :{ fish11_SetIniIntValue process_outgoing 1 | echo $color(Mode text) -at *** FiSH: outgoing message encryption enabled } + ...Disable :{ fish11_SetIniIntValue process_outgoing 0 | echo $color(Mode text) -at *** FiSH: outgoing message encryption disabled } + ..Decrypt incoming [Status] + ...Enable :{ fish11_SetIniIntValue process_incoming 1 | echo $color(Mode text) -at *** FiSH: incoming message decryption enabled } + ...Disable :{ fish11_SetIniIntValue process_incoming 0 | echo $color(Mode text) -at *** FiSH: incoming message decryption disabled } + ..- + ..Crypt-mark (Incoming) + ...Prefix :{ fish11_SetIniIntValue mark_position 2 | echo $color(Mode text) -at *** FiSH: encryption mark set to prefix } + ...Suffix :{ fish11_SetIniIntValue mark_position 1 | echo $color(Mode text) -at *** FiSH: encryption mark set to suffix } + ...Disable :{ fish11_SetIniIntValue mark_position 0 | echo $color(Mode text) -at *** FiSH: encryption mark disabled } + ..Crypt-mark (Outgoing) $+ $chr(32) $+ %mark_outgoing + ...Enable :set %mark_outgoing [On] + ...Disable :set %mark_outgoing [Off] + ...- + ...Style 1 :{ + set %mark_style 1 + set %mark_outgoing [On] + echo $color(Mode text) -at *** FiSH: outgoing mark style set to 1 (suffix) + } + ...Style 2 :{ + set %mark_style 2 + set %mark_outgoing [On] + echo $color(Mode text) -at *** FiSH: outgoing mark style set to 2 (prefix) + } + ...Style 3 :{ + set %mark_style 3 + set %mark_outgoing [On] + echo $color(Mode text) -at *** FiSH: outgoing mark style set to 3 (colored brackets) + } + ..NickTracker $+ $chr(32) $+ %NickTrack + ...Enable :{ set %NickTrack [On] | echo $color(Mode text) -at *** FiSH: nick tracking enabled } + ...Disable :{ set %NickTrack [Off] | echo $color(Mode text) -at *** FiSH: nick tracking disabled } + ..Encrypt NOTICE [Status] + ...Enable :{ fish11_SetIniIntValue encrypt_notice 1 | echo $color(Mode text) -at *** FiSH: NOTICE encryption enabled } + ...Disable :{ fish11_SetIniIntValue encrypt_notice 0 | echo $color(Mode text) -at *** FiSH: NOTICE encryption disabled } + ..Encrypt ACTION [Status] + ...Enable :{ fish11_SetIniIntValue encrypt_action 1 | echo $color(Mode text) -at *** FiSH: ACTION encryption enabled } + ...Disable :{ fish11_SetIniIntValue encrypt_action 0 | echo $color(Mode text) -at *** FiSH: ACTION encryption disabled } + ..No legacy FiSH 10 [Status] + ...Enable :{ fish11_SetIniIntValue no_fish10_legacy 1 | echo $color(Mode text) -at *** FiSH: legacy FiSH 10 compatibility disabled } + ...Disable :{ fish11_SetIniIntValue no_fish10_legacy 0 | echo $color(Mode text) -at *** FiSH: legacy FiSH 10 compatibility enabled } + ..- + ..Open config file :fish11_ViewIniFile + ..- + ..FiSH_11 - secure IRC encryption :shell -o https://github.com/ggielly/fish_11 + .Backup and restore + ..Create backup now :fish11_ScheduleBackup + ..Restore from backup :echo $color(Error) -at *** FiSH: restore functionality not yet implemented in DLL + ..Schedule daily backup :echo $color(Error) -at *** FiSH: scheduled backup not yet implemented in DLL + ..Stop scheduled backups :echo $color(Error) -at *** FiSH: scheduled backup not yet implemented in DLL + .Debug + ..Show debug info :fish11_debug + ..View INI file :fish11_ViewIniFile + ..Show encryption stats :fish11_stats + + - + FiSH_10 legacy compatibility + .DH1080 key exchange :fish10_keyx $active + .Show legacy key :fish10_showkey $active + .Set legacy key... :{ var %key = $?="Enter hex Blowfish key (4-56 bytes):" | if (%key != $null) fish10_setkey $active %key } + .Remove legacy key :fish10_delkey $active + .- + .Set topic (encrypted) :{ + ; Only allow in channel windows + if ($window($active).type != channel) { + echo $color(Mode text) -at *** FiSH_10: etopic can only be used in channel windows + return + } + var %topic = $?="Enter encrypted topic for " $+ $active $+ ":" + if (%topic != $null) etopic %topic + } + .- + .About FiSH_10 compatibility :{ + echo $color(Mode text) -at *** FiSH_10 Legacy Compatibility + echo $color(Mode text) -at *** Supports DH1080 key exchange and Blowfish ECB encryption + echo $color(Mode text) -at *** Compatible with mIRC FiSH 10.x and other FiSH implementations + echo $color(Mode text) -at *** Use DH1080 for automatic key exchange or set keys manually + } +} + + +; Window context menus +menu @fishdebug { + &Copy to Clipboard: fishdebug.clip + - + &Refresh:{ clear @fishdebug | fish11_debug } + C&lose:{ window -c @fishdebug } +} + + +menu @iniviewer { + &Save Changes:{ + var %temp_file = $+($mircdir, fish_11.tmp) + var %backup_file = %fish_config_file $+ .bak + + ; Write the content of the window to a temporary file + .remove %temp_file + var %i = 1 + while (%i <= $line(@iniviewer, 0)) { + write %temp_file $line(@iniviewer, %i) + inc %i + } + + if (!$isfile(%temp_file)) { + echo $color(Error) -at *** FiSH: error writing temporary file. Save aborted. + return + } + + ; Safely replace the old config file with the new one + .rename %fish_config_file %backup_file + .rename %temp_file %fish_config_file + + if ($isfile(%fish_config_file)) { + echo $color(Mode text) -at *** FiSH: configuration saved + .remove %backup_file + } else { + echo $color(Error) -at *** FiSH: error saving config, restoring from backup. + .rename %backup_file %fish_config_file + } + } + &Refresh:{ + clear @iniviewer + var %i = 1 + while (%i <= $lines(%fish_config_file)) { + aline @iniviewer $read(%fish_config_file, %i) + inc %i + } + } + - + C&lose:{ window -c @iniviewer } +} diff --git a/scripts/mirc/modules/fish11_startup.mrc b/scripts/mirc/modules/fish11_startup.mrc new file mode 100644 index 0000000..7a65533 --- /dev/null +++ b/scripts/mirc/modules/fish11_startup.mrc @@ -0,0 +1,109 @@ +;*********************** +;* FiSH_11 Startup * +;*********************** +; Initialization and DLL loading for FiSH_11 +; Written by GuY, 2025. Licensed under GPL-v3. + +; === INITIALIZATION AND STARTUP === +on *:START: { + .fish11_startup +} + +alias fish11_startup { + echo 4 -a *** FiSH_11 SECURITY NOTICE *** This script relies on 2 external DLL files. Only use trusted, signed versions from official sources. *** + echo 4 -a *** FiSH_11 SECURITY NOTICE *** Never run this script if you suspect your system has been compromised. *** + + var %exe_dir = $nofile($mircexe) + + ; Set paths to DLLs + %Fish11InjectDllFile = $qt(%exe_dir $+ fish_11_inject.dll) + %Fish11DllFile = $qt(%exe_dir $+ fish_11.dll) + + echo 4 -a DEBUG : loading DLLs... + echo 4 -a DEBUG : Fish11_InjectDllFile = %Fish11InjectDllFile + echo 4 -a DEBUG : Fish11_DllFile = %Fish11DllFile + + ; Check if DLLs exist + if (!$exists(%Fish11InjectDllFile)) { + echo 4 -a *** FiSH_11 ERROR: inject DLL not found: %Fish11InjectDllFile + halt + } + + if (!$exists(%Fish11DllFile)) { + echo 4 -a *** FiSH_11 ERROR: DLL not found: %Fish11DllFile + halt + } + + ; Check mIRC's DLL lock + if ($lock(dll)) { + echo 4 -a *** FiSH_11 ERROR: mIRC DLLs are locked. Enable DLLs in mIRC settings. + halt + } + + ; Initialize hash table for tracking key exchanges (X25519) + if (!$hget(fish11.dh).size) { + hmake fish11.dh 10 + } + + ; Initialize hash table for tracking legacy DH1080 key exchanges + if (!$hget(fish10.dh).size) { + hmake fish10.dh 10 + } + + ; Set configuration path in DLL + echo 4 -a DEBUG : calling fish_11.dll FiSH11_SetMircDir to set configuration path... + noop $dll(%Fish11DllFile, FiSH11_SetMircDir, $mircdir) + echo 4 -a DEBUG : MIRCDIR set to: $mircdir + + ; Initialize config file path + %fish_config_file = $+(%exe_dir, fish_11.ini) + + ; Get and display inject DLL version + var %inject_version = $dll(%Fish11InjectDllFile, FiSH11_InjectVersion, $null) + if (%inject_version) { + echo -ts *** %inject_version *** + } + else { + echo -ts *** FiSH_11: WARNING - could not load inject DLL version *** + } + + ; Get and display core DLL version + var %raw_version_info = $dll(%Fish11DllFile, FiSH11_GetVersion, $null) + + if (%raw_version_info) { + ; Parse the raw string: VERSION|BUILD_TYPE + ; 124 is ASCII for | + var %version_string = $gettok(%raw_version_info, 1, 124) + var %build_type = $gettok(%raw_version_info, 2, 124) + + ; Display the base version info + echo -ts *** %version_string *** + + ; Display context-specific warning or info message + if (%build_type == DEBUG) { + echo 4 -ts $chr(3)4 *** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING *** + echo 4 -ts $chr(3)4 *** + echo 4 -ts $chr(3)4 *** SECURITY WARNING : you're running a DEBUG version which logs EVERYTHING (keys, private messages, etc.) ON YOUR DISK. + echo 4 -ts $chr(3)4 *** DO NOT USE THIS VERSION IN REAL LIFE. + echo 4 -ts $chr(3)4 *** + echo 4 -ts $chr(3)4 *** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING *** + } + else { + echo 4 -ts $chr(3)3 *** You are running a RELEASE version. Sensitive data is NOT logged. + } + } + else { + echo -ts *** FiSH_11: WARNING - could not load core DLL version *** + } + + ; Initialize default settings if not already set + if (%autokeyx == $null) { set %autokeyx [Off] } + if (%mark_outgoing == $null) { set %mark_outgoing [Off] } + if (%mark_style == $null) { set %mark_style 1 } + if (%NickTrack == $null) { set %NickTrack [Off] } + ; Key exchange timeout (seconds) - keep in sync with DLL constant; can be overridden by user + if (%KEY_EXCHANGE_TIMEOUT_SECONDS == $null) { set %KEY_EXCHANGE_TIMEOUT_SECONDS 10 } + + ; Check if master key is unlocked, if not prompt user + .fish11_check_masterkey +} diff --git a/scripts/mirc/modules/fish11_utils.mrc b/scripts/mirc/modules/fish11_utils.mrc new file mode 100644 index 0000000..205ed07 --- /dev/null +++ b/scripts/mirc/modules/fish11_utils.mrc @@ -0,0 +1,468 @@ +;******************************* +;* FiSH_11 Utilities * +;******************************* +; Helper functions, debug, backup, and utility aliases +; Written by GuY, 2025. Licensed under GPL-v3. + +; === WINDOW ACTIVATION HANDLER === +on *:ACTIVE:*: { + if ($window($active).type isin query channel) { + fish11_UpdateStatusIndicator + } +} + + +; === STATUS INDICATOR === +; Status indicator update +alias fish11_UpdateStatusIndicator { + var %active = $active + + ; Don't update for non-chat windows + if (!$window(%active).type) || ($window(%active).type !isin query channel) return + + ; Check if we have a key for this target + var %key = $dll(%Fish11DllFile, FiSH11_FileGetKey, %active) + + ; Update status text accordingly + if ($len(%key) > 1) { + var %colored_fp = $fish11_GetColoredFingerprint(%active) + + if (!$window(@FiSH_Status)) { window -hn @FiSH_Status } + aline -p @FiSH_Status * $timestamp $+ %active is encrypted (Key: %colored_fp $+ ) + + ; Show encryption in status bar with colored fingerprint + echo -at ** FiSH_11: %active [Fingerprint: %colored_fp $+ ] + } + else { + if (!$window(@FiSH_Status)) { window -hn @FiSH_Status } + aline -p @FiSH_Status * $timestamp $+ %active is not encrypted + echo -at *** FiSH_11: %active [No encryption] + + ; Clear any stored fingerprint + unset %fish11.lastfingerprint. $+ [ %active ] + } +} + + +; === INI CONFIG HELPERS === +; For string values (plain_prefix, mark_encrypted, encryption_prefix) +alias fish11_GetIniValue { + return $dll(%Fish11DllFile, INI_GetString, $1 $2-) +} + +alias fish11_SetIniValue { + noop $dll(%Fish11DllFile, INI_SetString, $1 $2-) +} + +; For boolean/integer values (process_outgoing, process_incoming, encrypt_notice, etc.) +alias fish11_GetIniBoolValue { + return $dll(%Fish11DllFile, INI_GetBool, $1 $2-) +} + +alias fish11_SetIniIntValue { + noop $dll(%Fish11DllFile, INI_SetInt, $1 $2-) +} + +alias fish11_GetChannelIniValue { + return $dll(%Fish11DllFile, INI_GetString, channel_ $+ $1 $+ _ $+ $2 $3-) +} + +alias fish11_SetChannelIniValue { + noop $dll(%Fish11DllFile, INI_SetString, channel_ $+ $1 $+ _ $+ $2 $3-) +} + + +; === PLAIN PREFIX === +; Set plain message prefix +alias fish11_prefix { + if ($1 != $null) { + ; Add quotes for INI string value + var %value = " $+ $1- $+ " + fish11_SetIniValue plain_prefix %value + echo $color(Mode text) -at *** FiSH: plain-prefix set to $1- + } +} + + +; === BACKUP FUNCTIONALITY === +; Note: FiSH11_BackupKeys/RestoreKeys not yet implemented in DLL +; This is a placeholder for future implementation +alias fish11_ScheduleBackup { + echo $color(Error) -at *** FiSH: backup functionality not yet implemented in DLL + echo $color(Mode text) -at *** FiSH: use /fish11_file_list_keys to export keys manually +} + + +; === HELP AND VERSION === +; Show help and version information +alias fish11_help { + var %helpText + if ($dll(%Fish11DllFile, FiSH11_Help, &%helpText)) { + echo $color(Mode text) -at *** FiSH help: %helpText + } + else { + echo $color(Mode text) -at *** FiSH: help information unavailable + } + + ; Add Master Key help + echo $color(Mode text) -at $chr(160) + echo $color(Mode text) -at *** FiSH_11 Master Key commands: + echo $color(Mode text) -at *** /fish11_unlock [password] - Unlock master key (encrypts config/logs) + echo $color(Mode text) -at *** /fish11_lock - Lock master key (clears from memory) + echo $color(Mode text) -at *** /fish11_masterkey_status - Show master key status + echo $color(Mode text) -at *** When unlocked: configuration and logs are encrypted with Argon2id + ChaCha20-Poly1305 + echo $color(Mode text) -at *** When locked: configuration and logs are stored in plaintext + + ; Add Key TTL help + echo $color(Mode text) -at $chr(160) + echo $color(Mode text) -at *** FiSH_11 Key TTL (Expiration): + echo $color(Mode text) -at *** /fish11_keyttl - Show remaining lifetime of exchange key + echo $color(Mode text) -at *** Shorthand: /fkeyttl + echo $color(Mode text) -at *** Exchange keys expire after 24 hours; manually set keys never expire + + ; Add FCEP-1 help + echo $color(Mode text) -at $chr(160) + echo $color(Mode text) -at *** FiSH_11 FCEP-1 (Channel Encryption v1) commands: + echo $color(Mode text) -at *** /fish11_initchannel <#channel> [nick2] ... - Initialize encrypted channel + echo $color(Mode text) -at *** Shorthand: /fcep or /chankey + echo $color(Mode text) -at *** Example: /fish11_initchannel #secret alice bob charlie + echo $color(Mode text) -at *** Note: all members must have pre-shared keys with you first + echo $color(Mode text) -at $chr(160) + echo $color(Mode text) -at *** FCEP-1 automatically decrypts incoming channel messages + echo $color(Mode text) -at *** Channel names are case-insensitive (#Secret = #secret) + + ; Add incoming decryption help + echo $color(Mode text) -at $chr(160) + echo $color(Mode text) -at *** FiSH_11 Incoming Decryption: + echo $color(Mode text) -at *** Incoming messages are decrypted automatically by fish_11_inject.dll + echo $color(Mode text) -at *** If inject DLL is not loaded, fallback handlers will attempt decryption + echo $color(Mode text) -at *** /fish11_decrypt_msg - Manually decrypt a message + echo $color(Mode text) -at *** Shorthand: /fdec + echo $color(Mode text) -at *** Configure incoming decryption in fish_11.ini: process_incoming = 1 + + ; Add plaintext topic commands + echo $color(Mode text) -at $chr(160) + echo $color(Mode text) -at *** FiSH_11 Plaintext Topic commands: + echo $color(Mode text) -at *** /settopic <#channel> - Set a plaintext topic for a channel + echo $color(Mode text) -at *** /gettopic <#channel> - Get the plaintext topic for a channel + echo $color(Mode text) -at *** /removetopic <#channel> - Remove the plaintext topic for a channel + echo $color(Mode text) -at *** /etopic - Encrypt and set a topic for the current channel + echo $color(Mode text) -at $chr(160) + echo $color(Mode text) -at *** Plaintext topics are stored in the configuration file and can be retrieved later + + ; Add legacy fish10 topic commands + echo $color(Mode text) -at $chr(160) + echo $color(Mode text) -at *** FiSH_10 Legacy Topic commands: + echo $color(Mode text) -at *** /fish10_settopic <#channel> - Set a plaintext topic in legacy format + echo $color(Mode text) -at *** /fish10_gettopic <#channel> - Get a plaintext topic from legacy format + echo $color(Mode text) -at *** /fish10_removetopic <#channel> - Remove a plaintext topic from legacy format + echo $color(Mode text) -at $chr(160) + echo $color(Mode text) -at *** Legacy topics are stored in the configuration file with fish10 compatibility +} + +alias fish11_version { + var %raw_version_info = $dll(%Fish11DllFile, FiSH11_GetVersion, $null) + + if (!%raw_version_info) { + echo -ts *** FiSH_11: ERROR - could not get version info from DLL. + return + } + + ; Parse the raw string: VERSION|BUILD_TYPE + ; 124 is ASCII for | + var %version_string = $gettok(%raw_version_info, 1, 124) + var %build_type = $gettok(%raw_version_info, 2, 124) + + ; Display the base version info + echo -ts *** %version_string *** + + ; Display context-specific warning or info message + if (%build_type == DEBUG) { + echo 4 -ts $chr(3)4 *** SECURITY WARNING : you're running a DEBUG version which logs EVERYTHING (keys, private messages, etc.) ON YOUR DISK. + echo 4 -ts $chr(3)4 *** DO NOT USE THIS VERSION IN REAL LIFE. + } + else { + echo 4 -ts $chr(3)3 *** You are running a RELEASE version. Sensitive data is NOT logged. + echo 4 -ts $chr(3)3 *** Logging can be configured in your fish_11.ini file. + } +} + +alias fish11_injection_version { + var %inject_version = $dll(%Fish11InjectDllFile, FiSH11_InjectVersion, $null) + echo -ts *** %inject_version *** +} + + +; === DEBUG FUNCTIONALITY === +alias fish11_debug { + var %w = @fishdebug + var %a = aline -ph %w + + var %f1 = fishdebug + var %f2 = $rand(0,9999) + noop $iif($isfile(%Fish11DllFile),$dll(%Fish11DllFile,FiSH11_SetKey,$+($network,%f1,%f2,HelloWorld))) + + if (!$window(%w)) { + window -a %w -1 -1 550 300 Courier New 12 + } + else { + clear %w + window -a %w + } + + %a ---------FISH DEBUG--------- + %a $cr + %a ::VERSION + %a mIRC version: $version + %a SSL version: $sslversion + %a SSL ready: $sslready + %a SSL mode: $iif($readini($mircini,ssl,load),$v1,default) + %a $cr + %a ::mIRC + %a mIRC dir: $mircdir + %a mIRC.exe: $mircexe + %a mIRC.ini: $mircini + %a Portable: $iif($readini($mircini,about,portable),$v1,NotFound) + %a $cr + %a ::Files + %a fish_11.dll: %Fish11DllFile - $isfile(%Fish11DllFile) + %a version string: $iif($isfile(%Fish11DllFile),$dll(%Fish11DllFile,FiSH11_GetVersion),NotFound) + %a fish_11.toml: %fish_config_file - $isfile(%fish_config_file) + %a $cr + %a ::INI Configuration + %a Process incoming: $fish11_GetIniValue(process_incoming) + %a Process outgoing: $fish11_GetIniValue(process_outgoing) + %a Plain prefix: $fish11_GetIniValue(plain_prefix) + %a Mark position: $fish11_GetIniValue(mark_position) + %a Encrypt notice: $fish11_GetIniValue(encrypt_notice) + %a Encrypt action: $fish11_GetIniValue(encrypt_action) + %a No fish10 legacy: $fish11_GetIniValue(no_fish10_legacy) + %a $cr + %a ::Variables + %a fish_config_file: %fish_config_file + %a FiSH_dll: %Fish11DllFile + %a $cr + %a ::Testing + %a >> Writing key to config + %a << Reading back key, you should see a 'HelloWorld' on the next line. + %a !! FileGetKey: $iif($dll(%Fish11DllFile,FiSH11_FileGetKey, $+($network," ",%f1," ",%f2)),$v1,NotFound) + %a << Deleting key from config + noop $dll(%Fish11DllFile,FiSH11_FileDelKey,$+($network," ",%f1," ",%f2)) +} + + +; Debug: capture raw return from FiSH11_ExchangeKey and display hex/quoted output +alias fish11_debug_exchange { + if ($1 == $null) { echo 4 -a Usage: /fish11_debug_exchange | return } + var %nick = $1 + + ; Use $dll(...) to capture returned identifier string directly + var %raw_exch = $dll(%Fish11DllFile, FiSH11_ExchangeKey, %nick) + + ; Show quoted version so we can see whitespace/newlines + echo 4 -a *** FiSH_11 DEBUG: raw quoted return: $qt(%raw_exch) + + ; Replace common control chars with visible markers for quick glance + var %visible = $replace(%raw_exch, $chr(13) $+ $chr(10), , $chr(13), , $chr(10), , $chr(9), ) + echo 4 -a *** FiSH_11 DEBUG: visible: %visible + + ; Print decimal codes for each character (limit 200 chars to avoid flooding) + var %limited = $left(%raw_exch, 200) + var %codes = $null + var %i = 1 + while (%i <= $len(%limited)) { + var %c = $mid(%limited, %i, 1) + var %codes = %codes $+ $asc(%c) $+ " " + inc %i + } + echo 4 -a *** FiSH_11 DEBUG: decimal codes (first 200 chars): %codes +} + + +; === INI FILE VIEWER === +alias fish11_ViewIniFile { + var %w = @iniviewer + + if (!$isfile(%fish_config_file)) { + echo $color(Mode text) -at *** FiSH: config file not found: %fish_config_file + return + } + + if (!$window(%w)) { + window -a %w -1 -1 550 500 Courier New 10 + } + else { + clear %w + window -a %w + } + + titlebar %w FiSH 11 Configuration - %fish_config_file + + var %i = 1 + while (%i <= $lines(%fish_config_file)) { + aline %w $read(%fish_config_file, %i) + inc %i + } +} + + +; === HELPER FUNCTIONS === +alias -l fishdebug.clip { + clipboard + var %i = 1 + while ($line(@fishdebug,%i)) { + clipboard -an $v1 + inc %i + } +} + +alias statusmsg { + echo 4 -s [FiSH_11] $1- +} + + +; === INI HELPER FUNCTIONS (for compatibility with older scripts) === +alias INI_GetString { + ; Arguments: $1 = key, $2 = default value + var %config_path = $+($scriptdir,fish_11.ini) + var %value = $readini(%config_path, FiSH11, $1) + if (%value == $null) { return $2 } + return %value +} + +alias INI_GetInt { + ; Arguments: $1 = key, $2 = default value + var %config_path = $+($scriptdir,fish_11.ini) + var %value = $readini(%config_path, FiSH11, $1) + if (%value == $null) { return $2 } + return %value +} + + +; === TOPIC MANAGEMENT === + +; Encrypted topic alias - transparently encrypts the topic via the injection hook +alias etopic { + ; Check if we're in a channel window + if ($window($active).type != channel) { + echo $color(Mode text) -at *** FiSH_11: etopic can only be used in channel windows + return + } + + ; Check if there's a key for this channel (both traditional and FCEP-1) + var %channelKey = $dll(%Fish11DllFile, FiSH11_FileGetKey, $active) + ; Robust error check: if it doesn't look like a valid key or starts with error indicators + if ($left(%channelKey, 6) == Error: || $left(%channelKey, 3) == no ) { set %channelKey $null } + + var %hasLegacyKey = $dll(%Fish11DllFile, FiSH10_HasKey, $active) + + if (%channelKey == $null && %hasLegacyKey != 1) { + ; No key exists, but the engine may still try to encrypt if a channel key exists + ; This will be handled by the engine registration code + echo $color(Mode text) -at *** FiSH_11: no encryption key found for $active, topic will be sent in plain text + } else { + echo $color(Mode text) -at *** FiSH_11: topic will be encrypted for $active $iif(%hasLegacyKey == 1, (FiSH 10 legacy)) + } + + ; Execute the topic command - encryption will be handled by the engine + /topic $1- + + ; Clean up variables + unset %channelKey +} + +; Set a plaintext topic for a channel +alias settopic { + if ($1 == $null || $2- == $null) { + echo 4 -a Syntax: /settopic <#channel> + return + } + + var %channel = $1 + var %topic = $2- + + ; Validate channel name + if (!$regex(%channel, /^[#&]/)) { + echo $color(Error) -at *** FiSH_11 ERROR: Invalid channel name %channel (must start with # or &) + return + } + + var %result = $dll(%Fish11DllFile, FiSH11_SetTopic, $+(%channel, $chr(32), %topic)) + + if (%result && $left(%result, 6) != Error:) { + echo $color(Mode text) -at *** FiSH_11: %result + } + else { + var %error_msg = $iif(%result, %result, "Unknown error - could not set topic for %channel") + echo $color(Error) -at *** FiSH_11: error setting topic for %channel - %error_msg + } +} + +; Get a plaintext topic for a channel +alias gettopic { + if ($1 == $null) { + echo 4 -a Syntax: /gettopic <#channel> + return + } + + var %channel = $1 + + ; Validate channel name + if (!$regex(%channel, /^[#&]/)) { + echo $color(Error) -at *** FiSH_11 ERROR: Invalid channel name %channel (must start with # or &) + return + } + + var %result = $dll(%Fish11DllFile, FiSH11_GetTopic, %channel) + + if (%result && $left(%result, 6) != Error:) { + echo $color(Mode text) -at *** FiSH_11: Topic for %channel is: %result + } + else { + var %error_msg = $iif(%result, %result, "Unknown error - could not get topic for %channel") + echo $color(Error) -at *** FiSH_11: error getting topic for %channel - %error_msg + } +} + +; Remove a plaintext topic for a channel +alias removetopic { + if ($1 == $null) { + echo 4 -a Syntax: /removetopic <#channel> + return + } + + var %channel = $1 + + ; Validate channel name + if (!$regex(%channel, /^[#&]/)) { + echo $color(Error) -at *** FiSH_11 ERROR: Invalid channel name %channel (must start with # or &) + return + } + + var %result = $dll(%Fish11DllFile, FiSH11_RemoveTopic, %channel) + + if (%result && $left(%result, 6) != Error:) { + echo $color(Mode text) -at *** FiSH_11: %result + } + else { + var %error_msg = $iif(%result, %result, "Unknown error - could not remove topic for %channel") + echo $color(Error) -at *** FiSH_11: error removing topic for %channel - %error_msg + } +} + + +; === ENCRYPTION STATISTICS === +; Show encryption statistics +alias fish11_stats { + var %stats = $dll(%Fish11DllFile, FiSH11_GetEncryptionStats, $null) + if (%stats) { + echo $color(Mode text) -at *** FiSH_11 Encryption Statistics: + echo $color(Mode text) -at %stats + } + else { + echo $color(Error) -at *** FiSH_11: failed to retrieve encryption statistics + } +} + +; Short alias for statistics +alias fish_stats { fish11_stats }