def fish_secure(): global fish_secure_key, fish_secure_cipher fish_secure_key = weechat.config_string(fish_config_option["key"]) # if blank, do nothing if fish_secure_key == "": fish_success() return # if ${sec.data.fish}, check if sec.conf is decrypted # and decrypt elif fish_secure_key[:6] == "${sec.": decrypted = weechat.string_eval_expression(fish_secure_key, {}, {}, {}) if decrypted: fish_secure_cipher = Blowfish(decrypted) fish_decrypt_keys() fish_success() return else: global SCRIPT_NAME fish_secure_error() weechat.command(weechat.current_buffer(), "/wait 1ms /python unload %s" % SCRIPT_NAME) return # if key is neither ${sec.data.fish} or "" # encrypt/decrypt with user supplied, plain text key if fish_secure_key != "": fish_secure_cipher = Blowfish(fish_secure_key) fish_decrypt_keys() fish_success() return
def fish_secure_key_cb(data, option, value): global fish_secure_key, fish_secure_cipher fish_secure_key = weechat.config_string( weechat.config_get("fish.secure.key") ) if fish_secure_key == "": fish_secure_cipher = None return weechat.WEECHAT_RC_OK if fish_secure_key[:6] == "${sec.": decrypted = weechat.string_eval_expression( fish_secure_key, {}, {}, {} ) if decrypted: fish_secure_cipher = Blowfish(decrypted) return weechat.WEECHAT_RC_OK else: weechat.config_option_set(fish_config_option["key"], "", 0) weechat.prnt("", "Decrypt sec.conf first\n") return weechat.WEECHAT_RC_OK if fish_secure_key != "": fish_secure_cipher = Blowfish(fish_secure_key) return weechat.WEECHAT_RC_OK
def fish_modifier_out_topic_cb(data, modifier, server_name, string): global fish_keys, fish_cyphers match = re.match(r"^(TOPIC (.*?) :)(.*)$", string) if not match: return string if not match.group(3): return string target = "%s/%s" % (server_name, match.group(2)) targetl = ("%s/%s" % (server_name, match.group(2))).lower() buffer = weechat.info_get("irc_buffer", "%s,%s" % (server_name, match.group(2))) if targetl not in fish_keys: fish_announce_unencrypted(buffer, target) return string if targetl not in fish_cyphers: b = Blowfish(fish_keys[targetl]) fish_cyphers[targetl] = b else: b = fish_cyphers[targetl] cypher = blowcrypt_pack(match.group(3), b) fish_announce_encrypted(buffer, target) return "%s%s" % (match.group(1), cypher)
def fish_modifier_in_332_cb(data, modifier, server_name, string): global fish_keys, fish_cyphers match = re.match(r"^(?:@time=[\d:TZ.-]+\s)(:.*? 332 .*? (.*?) :)((\+OK |mcps )?.*)$", string) if not match: return string target = "%s/%s" % (server_name, match.group(2)) targetl = ("%s/%s" % (server_name, match.group(2))).lower() buffer = weechat.info_get("irc_buffer", "%s,%s" % (server_name, match.group(2))) if targetl not in fish_keys or not match.group(4): fish_announce_unencrypted(buffer, target) return string if targetl not in fish_cyphers: b = Blowfish(fish_keys[targetl]) fish_cyphers[targetl] = b else: b = fish_cyphers[targetl] clean = blowcrypt_unpack(match.group(3), b) fish_announce_encrypted(buffer, target) return "%s%s" % (match.group(1), fish_msg_w_marker(clean))
def fish_modifier_in_topic_cb(data, modifier, server_name, string): global fish_keys, fish_cyphers match = re.match(r"^(:.*?!.*? TOPIC (.*?) :)((\+OK |mcps )?.*)$", string) #match.group(0): message #match.group(1): msg without payload #match.group(2): channel #match.group(3): topic #match.group(4): +OK |mcps if not match: return string target = "%s/%s" % (server_name, match.group(2)) targetl = ("%s/%s" % (server_name, match.group(2))).lower() buffer = weechat.info_get("irc_buffer", "%s,%s" % (server_name, match.group(2))) if targetl not in fish_keys or not match.group(4): fish_announce_unencrypted(buffer, target) return string if targetl not in fish_cyphers: b = Blowfish(fish_keys[targetl]) fish_cyphers[targetl] = b else: b = fish_cyphers[targetl] clean = blowcrypt_unpack(match.group(3), b) fish_announce_encrypted(buffer, target) return "%s%s" % (match.group(1), fish_msg_w_marker(clean))
def fish_modifier_out_privmsg_cb(data, modifier, server_name, string): global fish_keys, fish_cyphers if type(string) is bytes: return string match = re.match(r"^(PRIVMSG (.*?) :)(.*)$", string) if not match: return string target = "%s/%s" % (server_name, match.group(2)) targetl = ("%s/%s" % (server_name, match.group(2))).lower() buffer = weechat.info_get("irc_buffer", "%s,%s" % (server_name, match.group(2))) if targetl not in fish_keys: fish_announce_unencrypted(buffer, target) return string if targetl not in fish_cyphers: b = Blowfish(fish_keys[targetl]) fish_cyphers[targetl] = b else: b = fish_cyphers[targetl] cypher = blowcrypt_pack(fish_msg_wo_marker(match.group(3)).encode(), b) fish_announce_encrypted(buffer, target) return "%s%s" % (match.group(1), cypher)
def fish_secure(): global fish_secure_key, fish_secure_cipher fish_secure_key = weechat.config_string(fish_config_option["key"]) # if blank, do nothing if fish_secure_key == "": return # if ${sec.data.fish}, check if sec.conf is decrypted # and decrypt elif fish_secure_key[:6] == "${sec.": decrypted = weechat.string_eval_expression(fish_secure_key, {}, {}, {}) if decrypted: fish_secure_cipher = Blowfish(decrypted) fish_decrypt_keys() return else: global SCRIPT_NAME message = ("\n%s%sblowkey:%s unable to recover key from sec.conf\n" "%s%sblowkey:%s fish.py %sNOT LOADED\n" "%s%sblowkey:%s decrypt secured data first\n" "%s%sblowkey:%s then reload fish.py\n\n") % ( weechat.prefix("error"), weechat.color("underline"), weechat.color("reset"), weechat.prefix("error"), weechat.color("underline"), weechat.color("reset"), weechat.color("*red"), weechat.prefix("error"), weechat.color("underline"), weechat.color("reset"), weechat.prefix("error"), weechat.color("underline"), weechat.color("reset")) weechat.prnt("", "%s" % message) weechat.command(weechat.current_buffer(), "/wait 1ms /python unload %s" % SCRIPT_NAME) return # if key is neither ${sec.data.fish} or "" # encrypt/decrypt with user supplied, plain text key if fish_secure_key != "": fish_secure_cipher = Blowfish(fish_secure_key) fish_decrypt_keys() return
def fish_modifier_in_privmsg_cb(data, modifier, server_name, string): global fish_keys, fish_cyphers match = re.match( r"^(:(.*?)!.*? PRIVMSG (.*?) :)(\x01ACTION )?((\+OK |mcps )?.*?)(\x01)?$", string, ) # match.group(0): message # match.group(1): msg without payload # match.group(2): source # match.group(3): target # match.group(4): action # match.group(5): msg # match.group(6): +OK |mcps if not match: return string if match.group(3) == weechat.info_get("irc_nick", server_name): dest = match.group(2) else: dest = match.group(3) target = "%s/%s" % (server_name, dest) targetl = ("%s/%s" % (server_name, dest)).lower() buffer = weechat.info_get("irc_buffer", "%s,%s" % (server_name, dest)) if not match.group(6): fish_announce_unencrypted(buffer, target) return string if targetl not in fish_keys: fish_announce_unencrypted(buffer, target) return string fish_announce_encrypted(buffer, target) if targetl not in fish_cyphers: b = Blowfish(fish_keys[targetl]) fish_cyphers[targetl] = b else: b = fish_cyphers[targetl] clean = blowcrypt_unpack(match.group(5), b) if not match.group(4): return "%s%s" % (match.group(1), fish_msg_w_marker(clean)) return "%s%s%s\x01" % ( match.group(1), match.group(4), fish_msg_w_marker(clean), )
def fish_secure_genkey(buffer): global fish_secure_cipher, fish_config_option newKey = blowcrypt_b64encode(urandom(32)) # test to see if sec.conf decrypted weechat.command(buffer, "/secure set fish test") decrypted = weechat.string_eval_expression("${sec.data.fish}", {}, {}, {}) if decrypted == "test": weechat.config_option_set(fish_config_option["key"], "${sec.data.fish}", 0) fish_secure_cipher = Blowfish(newKey) weechat.command(buffer, "/secure set fish %s" % newKey)
def fish_modifier_in_332_cb(data, modifier, server_name, string): global fish_keys, fish_cyphers if type(string) is bytes: return string match = re.match(r"^(:.*? 332 .*? (.*?) :)((\+OK |mcps )?.*)$", string) if not match: return string target = "%s/%s" % (server_name, match.group(2)) targetl = ("%s/%s" % (server_name, match.group(2))).lower() buffer = weechat.info_get("irc_buffer", "%s,%s" % (server_name, match.group(2))) if targetl not in fish_keys or not match.group(4): fish_announce_unencrypted(buffer, target) return string key = fish_keys[targetl] try: if targetl not in fish_cyphers: b = Blowfish(key) fish_cyphers[targetl] = b else: b = fish_cyphers[targetl] clean = blowcrypt_unpack(match.group(3), b, key) fish_announce_encrypted(buffer, target) return b"%s%s" % (match.group(1).encode(), fish_msg_w_marker(clean)) except Exception as e: fish_announce_unencrypted(buffer, target) raise e
def fish_modifier_in_notice_cb(data, modifier, server_name, string): global fish_DH1080ctx, fish_keys, fish_cyphers match = re.match( r"^(?:@time=[\d:TZ.-]+\s)?(:(.*?)!.*? NOTICE (.*?) :)((DH1080_INIT |DH1080_INIT_CBC |DH1080_FINISH |\+OK |mcps )?.*)$", string) #match.group(0): message #match.group(1): msg without payload #match.group(2): source #match.group(3): target #match.group(4): msg #match.group(5): DH1080_INIT |DH1080_INIT_CBC |DH1080_FINISH if not match or not match.group(5): return string if match.group(3) != weechat.info_get("irc_nick", server_name): return string target = "%s/%s" % (server_name, match.group(2)) targetl = ("%s/%s" % (server_name, match.group(2))).lower() buffer = weechat.info_get("irc_buffer", "%s,%s" % ( server_name, match.group(2))) if match.group(5) == "DH1080_FINISH " and targetl in fish_DH1080ctx: if not dh1080_unpack(match.group(4), fish_DH1080ctx[targetl]): fish_announce_unencrypted(buffer, target) return string msg = "Key exchange for %s successful" % target if fish_DH1080ctx[targetl].cbc: msg += " (CBC mode)" fish_alert(buffer, msg) fish_keys[targetl] = dh1080_secret(fish_DH1080ctx[targetl]) if targetl in fish_cyphers: del fish_cyphers[targetl] del fish_DH1080ctx[targetl] return "" if match.group(5).startswith("DH1080_INIT"): fish_DH1080ctx[targetl] = DH1080Ctx() msg = ' '.join(match.group(4).split()[0:3]) if not dh1080_unpack(msg, fish_DH1080ctx[targetl]): fish_announce_unencrypted(buffer, target) return string reply = dh1080_pack(fish_DH1080ctx[targetl]) msg = "Key exchange initiated by %s. Key set." % target if fish_DH1080ctx[targetl].cbc: msg += " (CBC mode)" fish_alert(buffer, msg) weechat.command(buffer, "/mute -all notice %s %s" % ( match.group(2), reply)) fish_keys[targetl] = dh1080_secret(fish_DH1080ctx[targetl]) if targetl in fish_cyphers: del fish_cyphers[targetl] del fish_DH1080ctx[targetl] return "" if match.group(5) in ["+OK ", "mcps "]: if targetl not in fish_keys: fish_announce_unencrypted(buffer, target) return string if targetl not in fish_cyphers: b = Blowfish(fish_keys[targetl]) fish_cyphers[targetl] = b else: b = fish_cyphers[targetl] clean = blowcrypt_unpack(match.group(4), b) fish_announce_encrypted(buffer, target) return "%s%s" % (match.group(1), fish_msg_w_marker(clean)) fish_announce_unencrypted(buffer, target) return string
def decrypt(key, inp): b = Blowfish(key) return blowcrypt_unpack(inp, b)
def encrypt(key, text): b = Blowfish(key) return blowcrypt_pack(text, b)
def new(key, mode=MODE_ECB, IV=None, counter=None, segment_size=None): """Create a new cipher object Blowfish using pycrypto for algo and pycryptoplus for ciphermode key = raw string containing the key mode = Blowfish.MODE_ECB/CBC/CFB/OFB/CTR/CMAC, default is ECB IV = IV as a raw string, default is "all zero" IV -> only needed for CBC mode counter = counter object (CryptoPlus.Util.util.Counter) -> only needed for CTR mode segment_size = amount of bits to use from the keystream in each chain part -> supported values: multiple of 8 between 8 and the blocksize of the cipher (only per byte access possible), default is 8 -> only needed for CFB mode EXAMPLES: ********** IMPORTING: ----------- >>> from CryptoPlus.Cipher import Blowfish ECB EXAMPLE: http://www.schneier.com/code/vectors.txt ------------- >>> cipher = Blowfish.new(('0131D9619DC1376E').decode('hex')) >>> ( cipher.encrypt(('5CD54CA83DEF57DA').decode('hex')) ).encode('hex') 'b1b8cc0b250f09a0' >>> ( cipher.decrypt((_).decode('hex')) ).encode('hex') '5cd54ca83def57da' CBC, CFB, OFB EXAMPLE: http://www.schneier.com/code/vectors.txt ---------------------- >>> key = ('0123456789ABCDEFF0E1D2C3B4A59687').decode('hex') >>> IV = ('FEDCBA9876543210').decode('hex') >>> plaintext = ('37363534333231204E6F77206973207468652074696D6520').decode('hex') >>> cipher = Blowfish.new(key,Blowfish.MODE_CBC,IV) >>> ciphertext = cipher.encrypt(plaintext) >>> (ciphertext).encode('hex').upper() '6B77B4D63006DEE605B156E27403979358DEB9E7154616D9' >>> key = '0123456789ABCDEFF0E1D2C3B4A59687'.decode('hex') >>> iv = 'FEDCBA9876543210'.decode('hex') >>> plaintext = '37363534333231204E6F77206973207468652074696D6520666F722000'.decode('hex') >>> cipher = Blowfish.new(key,Blowfish.MODE_CBC,iv) >>> ciphertext = cipher.encrypt(plaintext) >>> (ciphertext).encode('hex').upper() '6B77B4D63006DEE605B156E27403979358DEB9E7154616D9' >>> cipher = Blowfish.new(key,Blowfish.MODE_CFB,iv,segment_size=64) >>> ciphertext = cipher.encrypt(plaintext) >>> (ciphertext).encode('hex').upper() 'E73214A2822139CAF26ECF6D2EB9E76E3DA3DE04D1517200519D57A6C3' >>> cipher = Blowfish.new(key,Blowfish.MODE_OFB,iv) >>> ciphertext = cipher.encrypt(plaintext) >>> (ciphertext).encode('hex').upper() 'E73214A2822139CA62B343CC5B65587310DD908D0C241B2263C2CF80DA' """ return Blowfish(key, mode, IV, counter, segment_size)
def fish_modifier_in_privmsg_cb(data, modifier, server_name, string): global fish_keys, fish_cyphers if type(string) is bytes: return string match = re.match( r"^(:(.*?)!.*? PRIVMSG (.*?) :)(\x01ACTION )?" r"((\+OK |mcps )?.*?)(\x01)?$", string) # match.group(0): message # match.group(1): msg without payload # match.group(2): source # match.group(3): target # match.group(4): action # match.group(5): msg # match.group(6): "+OK "|"mcps " if not match: return string if match.group(3) == weechat.info_get("irc_nick", server_name): dest = match.group(2) else: dest = match.group(3) target = "%s/%s" % (server_name, dest) targetl = ("%s/%s" % (server_name, dest)).lower() buffer = weechat.info_get("irc_buffer", "%s,%s" % (server_name, dest)) if not match.group(6): fish_announce_unencrypted(buffer, target) return string if targetl not in fish_keys: fish_announce_unencrypted(buffer, target) return string key = fish_keys[targetl] try: if targetl not in fish_cyphers: b = Blowfish(key) fish_cyphers[targetl] = b else: b = fish_cyphers[targetl] clean = blowcrypt_unpack(match.group(5), b, key) fish_announce_encrypted(buffer, target) if not match.group(4): return b'%s%s' % (match.group(1).encode(), fish_msg_w_marker(clean)) return b"%s%s%s\x01" % (match.group(1).encode(), match.group(4).encode(), fish_msg_w_marker(clean)) except Exception as e: fish_announce_unencrypted(buffer, target) raise e
def fish_modifier_in_notice_cb(data, modifier, server_name, string): global fish_DH1080ctx, fish_keys, fish_cyphers if type(string) is bytes: return string match = re.match( r"^(:(.*?)!.*? NOTICE (.*?) :)" r"((DH1080_INIT |DH1080_FINISH |\+OK |mcps )?.*)$", string) # match.group(0): message # match.group(1): msg without payload # match.group(2): source # match.group(3): target # match.group(4): msg # match.group(5): "DH1080_INIT "|"DH1080_FINISH "|"+OK "|"mcps " if not match or not match.group(5): return string if match.group(3) != weechat.info_get("irc_nick", server_name): return string target = "%s/%s" % (server_name, match.group(2)) targetl = ("%s/%s" % (server_name, match.group(2))).lower() buffer = weechat.info_get("irc_buffer", "%s,%s" % (server_name, match.group(2))) if match.group(5) == "DH1080_FINISH " and targetl in fish_DH1080ctx: if not dh1080_unpack(match.group(4), fish_DH1080ctx[targetl]): fish_announce_unencrypted(buffer, target) return string fish_alert(buffer, "Key exchange for %s successful" % target) fish_keys[targetl] = dh1080_secret(fish_DH1080ctx[targetl]) if targetl in fish_cyphers: del fish_cyphers[targetl] del fish_DH1080ctx[targetl] return "" if match.group(5) == "DH1080_INIT ": fish_DH1080ctx[targetl] = DH1080Ctx() msg = ' '.join(match.group(4).split()[0:2]) if not dh1080_unpack(msg, fish_DH1080ctx[targetl]): fish_announce_unencrypted(buffer, target) return string reply = dh1080_pack(fish_DH1080ctx[targetl]) fish_alert(buffer, "Key exchange initiated by %s. Key set." % target) weechat.command(buffer, "/mute -all notice %s %s" % (match.group(2), reply)) fish_keys[targetl] = dh1080_secret(fish_DH1080ctx[targetl]) if targetl in fish_cyphers: del fish_cyphers[targetl] del fish_DH1080ctx[targetl] return "" if match.group(5) in ["+OK ", "mcps "]: if targetl not in fish_keys: fish_announce_unencrypted(buffer, target) return string key = fish_keys[targetl] try: if targetl not in fish_cyphers: b = Blowfish(key) fish_cyphers[targetl] = b else: b = fish_cyphers[targetl] clean = blowcrypt_unpack(match.group(4), b, key) fish_announce_encrypted(buffer, target) return b"%s%s" % (match.group(1).encode(), fish_msg_w_marker(clean)) except Exception as e: fish_announce_unencrypted(buffer, target) raise e fish_announce_unencrypted(buffer, target) return string