Add encryption.py NO DECRYPTION yet
All checks were successful
/ mirror (push) Successful in 5s

This commit is contained in:
stcb 2025-03-29 20:55:09 +02:00
parent 79b0491a75
commit 394143b4df
4 changed files with 280 additions and 108 deletions

View File

@ -260,7 +260,7 @@
</mxGraphModel>
</diagram>
<diagram id="4Sb7mgJDpsadGym-U4wz" name="Echanges">
<mxGraphModel dx="1062" dy="587" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<mxGraphModel dx="1195" dy="683" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
@ -403,7 +403,7 @@
<mxGeometry x="6.25" y="32.5" width="67.5" height="25" as="geometry" />
</mxCell>
<mxCell id="pP7SjZfcCiBg3d1TCkzP-58" value="" style="html=1;shadow=0;dashed=0;align=center;verticalAlign=middle;shape=mxgraph.arrows2.arrow;dy=0;dx=10;notch=0;" parent="1" vertex="1">
<mxGeometry x="160" y="1160" width="360" height="230" as="geometry" />
<mxGeometry x="160" y="1160" width="450" height="200" as="geometry" />
</mxCell>
<mxCell id="pP7SjZfcCiBg3d1TCkzP-59" value="ENCRYPTED COMS" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontSize=23;" parent="1" vertex="1">
<mxGeometry x="305" y="1100" width="240" height="40" as="geometry" />
@ -456,53 +456,55 @@
<mxCell id="pP7SjZfcCiBg3d1TCkzP-77" value="=1096b" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="327.5" y="970" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-1" value="Checksum" style="swimlane;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="180" y="1280" width="65" height="80" as="geometry" />
<mxCell id="_H5URFloX_BVB2BL7kO6-1" value="CRC ?" style="swimlane;whiteSpace=wrap;html=1;fillColor=#008a00;fontColor=#ffffff;strokeColor=#005700;" parent="1" vertex="1">
<mxGeometry x="375" y="1270" width="63.25" height="60" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-2" value="CRC-32" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="_H5URFloX_BVB2BL7kO6-1" vertex="1">
<mxGeometry x="2.5" y="30" width="60" height="30" as="geometry" />
<mxGeometry x="1.6199999999999992" y="25" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-3" value="Flag" style="swimlane;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="180" y="1170" width="65" height="80" as="geometry" />
<mxCell id="_H5URFloX_BVB2BL7kO6-3" value="Flag" style="swimlane;whiteSpace=wrap;html=1;fillColor=#008a00;fontColor=#ffffff;strokeColor=#005700;" parent="1" vertex="1">
<mxGeometry x="180" y="1170" width="65" height="60" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-4" value="&quot;IEM&quot;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="_H5URFloX_BVB2BL7kO6-3" vertex="1">
<mxGeometry x="2.5" y="30" width="60" height="30" as="geometry" />
<mxCell id="_H5URFloX_BVB2BL7kO6-4" value="To determine" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="_H5URFloX_BVB2BL7kO6-3" vertex="1">
<mxGeometry x="2.5" y="25" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-5" value="nbretry" style="swimlane;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="253" y="1170" width="65" height="80" as="geometry" />
<mxGeometry x="344.38" y="1170" width="65" height="60" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-6" value="0&lt;span style=&quot;color: rgba(0, 0, 0, 0); font-family: monospace; font-size: 0px; text-align: start; text-wrap-mode: nowrap;&quot;&gt;%3CmxGraphModel%3E%3Croot%3E%3CmxCell%20id%3D%220%22%2F%3E%3CmxCell%20id%3D%221%22%20parent%3D%220%22%2F%3E%3CmxCell%20id%3D%222%22%20value%3D%22Flag%22%20style%3D%22swimlane%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22180%22%20y%3D%221090%22%20width%3D%2265%22%20height%3D%2280%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%223%22%20value%3D%22%26quot%3BIEM%26quot%3B%22%20style%3D%22text%3Bhtml%3D1%3Balign%3Dcenter%3BverticalAlign%3Dmiddle%3BwhiteSpace%3Dwrap%3Brounded%3D0%3B%22%20vertex%3D%221%22%20parent%3D%222%22%3E%3CmxGeometry%20x%3D%222.5%22%20y%3D%2230%22%20width%3D%2260%22%20height%3D%2230%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3C%2Froot%3E%3C%2FmxGraphModel%3E&lt;/span&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="_H5URFloX_BVB2BL7kO6-5" vertex="1">
<mxGeometry x="2.5" y="30" width="60" height="30" as="geometry" />
<mxCell id="_H5URFloX_BVB2BL7kO6-6" value="y" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="_H5URFloX_BVB2BL7kO6-5" vertex="1">
<mxGeometry x="2.5" y="25" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-7" value="msg_len" style="swimlane;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="325" y="1170" width="65" height="80" as="geometry" />
<mxCell id="_H5URFloX_BVB2BL7kO6-7" value="msg_len" style="swimlane;whiteSpace=wrap;html=1;fillColor=#008a00;fontColor=#ffffff;strokeColor=#005700;" parent="1" vertex="1">
<mxGeometry x="262.5" y="1170" width="65" height="60" as="geometry">
<mxRectangle x="262.5" y="1170" width="90" height="30" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-8" value="XXX" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="_H5URFloX_BVB2BL7kO6-7" vertex="1">
<mxGeometry x="2.5" y="25" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-9" value="msg" style="swimlane;whiteSpace=wrap;html=1;fillColor=#0050ef;fontColor=#ffffff;strokeColor=#001DBC;" parent="1" vertex="1">
<mxGeometry x="187.5" y="1270" width="65" height="60" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-10" value="BBB" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="_H5URFloX_BVB2BL7kO6-9" vertex="1">
<mxGeometry x="2.5" y="30" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-9" value="msg" style="swimlane;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="423.75" y="1170" width="65" height="80" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-10" value="XXX" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="_H5URFloX_BVB2BL7kO6-9" vertex="1">
<mxGeometry x="2.5" y="30" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-11" value="24b" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="180" y="1250" width="60" height="30" as="geometry" />
<mxCell id="_H5URFloX_BVB2BL7kO6-11" value="16b" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="180" y="1230" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-12" value="8b" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="258" y="1250" width="55" height="30" as="geometry" />
<mxGeometry x="349.38" y="1230" width="55" height="30" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-13" value="16b" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="330" y="1250" width="55" height="30" as="geometry" />
<mxGeometry x="267.5" y="1230" width="55" height="30" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-14" value="yyy" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="428.75" y="1250" width="55" height="30" as="geometry" />
<mxCell id="_H5URFloX_BVB2BL7kO6-14" value="96b" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="510" y="1230" width="55" height="30" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-15" value="32b" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="185" y="1360" width="55" height="30" as="geometry" />
<mxGeometry x="379.12" y="1330" width="55" height="30" as="geometry" />
</mxCell>
<mxCell id="_H5URFloX_BVB2BL7kO6-16" value="=80b + yyy" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="389.38" y="1330" width="100" height="30" as="geometry" />
<mxCell id="_H5URFloX_BVB2BL7kO6-16" value="= (180b ~ 212b) + yyy" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="465" y="1285" width="130" height="30" as="geometry" />
</mxCell>
<mxCell id="pWkGvNQAXuiST1IiWYlx-2" value="Cypher" style="swimlane;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="375" y="130" width="58.75" height="80" as="geometry" />
@ -522,6 +524,42 @@
<mxCell id="pWkGvNQAXuiST1IiWYlx-7" value="4b" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="601.88" y="350" width="55" height="30" as="geometry" />
</mxCell>
<mxCell id="pWkGvNQAXuiST1IiWYlx-8" value="status" style="swimlane;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="425" y="1170" width="65" height="60" as="geometry" />
</mxCell>
<mxCell id="pWkGvNQAXuiST1IiWYlx-9" value="CRC ?" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="pWkGvNQAXuiST1IiWYlx-8">
<mxGeometry x="2.5" y="25" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="pWkGvNQAXuiST1IiWYlx-10" value="4b" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="428.75" y="1230" width="55" height="30" as="geometry" />
</mxCell>
<mxCell id="pWkGvNQAXuiST1IiWYlx-11" value="iv" style="swimlane;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="505" y="1170" width="65" height="60" as="geometry" />
</mxCell>
<mxCell id="pWkGvNQAXuiST1IiWYlx-12" value="random&lt;div&gt;(+Z)&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="pWkGvNQAXuiST1IiWYlx-11">
<mxGeometry x="2.5" y="25" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="pWkGvNQAXuiST1IiWYlx-13" value="BBB b" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="193" y="1330" width="55" height="30" as="geometry" />
</mxCell>
<mxCell id="pWkGvNQAXuiST1IiWYlx-14" value="MAC" style="swimlane;whiteSpace=wrap;html=1;fillColor=#008a00;fontColor=#ffffff;strokeColor=#005700;" vertex="1" parent="1">
<mxGeometry x="286.13" y="1270" width="63.25" height="60" as="geometry" />
</mxCell>
<mxCell id="pWkGvNQAXuiST1IiWYlx-15" value="AEAD" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="pWkGvNQAXuiST1IiWYlx-14">
<mxGeometry x="1.6199999999999992" y="25" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="pWkGvNQAXuiST1IiWYlx-16" value="128b" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="290.25" y="1330" width="55" height="30" as="geometry" />
</mxCell>
<mxCell id="pWkGvNQAXuiST1IiWYlx-17" value="Green = clear data" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#008a00;fontColor=#ffffff;strokeColor=#005700;" vertex="1" parent="1">
<mxGeometry x="10" y="1170" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="pWkGvNQAXuiST1IiWYlx-18" value="&lt;font style=&quot;color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;White = additional data&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=none;strokeColor=light-dark(#6C8EBF,#FFFFFF);" vertex="1" parent="1">
<mxGeometry y="1220" width="130" height="30" as="geometry" />
</mxCell>
<mxCell id="pWkGvNQAXuiST1IiWYlx-19" value="Blue = encrypted data" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#0050ef;fontColor=#ffffff;strokeColor=#001DBC;" vertex="1" parent="1">
<mxGeometry x="10" y="1270" width="110" height="30" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>

View File

@ -14,7 +14,6 @@ def main():
print(f"{YELLOW}\n======================================")
print(" Icing Protocol - Manual CLI Demo ")
print("======================================\n" + RESET)
print(f"Listening on port: {protocol.local_port}")
print(f"Your identity public key (hex): {protocol.identity_pubkey.hex()}")
print("\nAvailable commands:")
@ -22,91 +21,90 @@ def main():
print(" connect <port>")
print(" generate_ephemeral_keys")
print(" send_ping")
print(" respond_ping <index> <0|1>")
print(" send_handshake")
print(" respond_ping <index> <0|1>")
print(" generate_ecdhe <index>")
print(" derive_hkdf")
print(" send_encrypted <plaintext>")
print(" decrypt_message <hex_message>")
print(" auto_responder <on|off>")
print(" show_state")
print(" exit\n")
while True:
while True:
try:
line = input("Cmd> ").strip()
except EOFError:
break
if not line:
try:
line = input("Cmd> ").strip()
except EOFError:
break
if not line:
continue
parts = line.split()
cmd = parts[0].lower()
if cmd == "exit":
protocol.stop()
break
elif cmd == "show_state":
protocol.show_state()
elif cmd == "set_peer_identity":
if len(parts) != 2:
print("Usage: set_peer_identity <hex_pubkey>")
continue
parts = line.split()
cmd = parts[0].lower()
if cmd == "exit":
protocol.stop()
sys.exit(0)
elif cmd == "show_state":
protocol.show_state()
elif cmd == "set_peer_identity":
if len(parts) != 2:
print("Usage: set_peer_identity <hex_pubkey>")
continue
protocol.set_peer_identity(parts[1])
elif cmd == "connect":
if len(parts) != 2:
print("Usage: connect <port>")
continue
try:
port = int(parts[1])
protocol.connect_to_peer(port)
except ValueError:
print("Invalid port.")
elif cmd == "generate_ephemeral_keys":
protocol.generate_ephemeral_keys()
elif cmd == "send_ping":
protocol.send_ping_request()
elif cmd == "send_handshake":
protocol.send_handshake()
elif cmd == "respond_ping":
if len(parts) != 3:
print("Usage: respond_ping <index> <0|1>")
continue
try:
idx = int(parts[1])
ac = int(parts[2])
protocol.respond_to_ping(idx, ac)
except ValueError:
print("Index and answer must be integers.")
elif cmd == "generate_ecdhe":
if len(parts) != 2:
print("Usage: generate_ecdhe <index>")
continue
try:
idx = int(parts[1])
protocol.generate_ecdhe(idx)
except ValueError:
print("Index must be an integer.")
elif cmd == "derive_hkdf":
protocol.derive_hkdf()
elif cmd == "auto_responder":
if len(parts) != 2:
print("Usage: auto_responder <on|off>")
continue
arg = parts[1].lower()
protocol.enable_auto_responder(arg == "on")
else:
print(f"{RED}[ERROR]{RESET} Unknown command: {cmd}")
protocol.set_peer_identity(parts[1])
elif cmd == "connect":
if len(parts) != 2:
print("Usage: connect <port>")
continue
try:
port = int(parts[1])
protocol.connect_to_peer(port)
except ValueError:
print("Invalid port.")
elif cmd == "generate_ephemeral_keys":
protocol.generate_ephemeral_keys()
elif cmd == "send_ping":
protocol.send_ping_request()
elif cmd == "send_handshake":
protocol.send_handshake()
elif cmd == "respond_ping":
if len(parts) != 3:
print("Usage: respond_ping <index> <0|1>")
continue
try:
idx = int(parts[1])
ac = int(parts[2])
protocol.respond_to_ping(idx, ac)
except ValueError:
print("Index and answer must be integers.")
elif cmd == "generate_ecdhe":
if len(parts) != 2:
print("Usage: generate_ecdhe <index>")
continue
try:
idx = int(parts[1])
protocol.generate_ecdhe(idx)
except ValueError:
print("Index must be an integer.")
elif cmd == "derive_hkdf":
protocol.derive_hkdf()
elif cmd == "send_encrypted":
if len(parts) < 2:
print("Usage: send_encrypted <plaintext>")
continue
# Join the rest of the line as plaintext
plaintext = " ".join(parts[1:])
protocol.send_encrypted_message(plaintext)
elif cmd == "decrypt_message":
if len(parts) != 2:
print("Usage: decrypt_message <hex_message>")
continue
protocol.decrypt_encrypted_message(parts[1])
elif cmd == "auto_responder":
if len(parts) != 2:
print("Usage: auto_responder <on|off>")
continue
protocol.enable_auto_responder(parts[1].lower() == "on")
else:
print(f"{RED}[ERROR]{RESET} Unknown command: {cmd}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,94 @@
import os
import struct
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
class MessageHeader:
"""
Represents the header of an encrypted message.
- flag (16 bits)
- data_len (16 bits): length in bytes of the encrypted payload (excluding tag)
- Associated Data (AD):
* retry (8 bits)
* connexion_status (4 bits) + 4 bits padding (packed in one byte)
* iv/messageID (96 bits / 12 bytes)
Total header size: 2 + 2 + 1 + 1 + 12 = 18 bytes.
"""
def __init__(self, flag: int, data_len: int, retry: int, connexion_status: int, iv: bytes):
self.flag = flag # 16 bits
self.data_len = data_len # 16 bits
self.retry = retry # 8 bits
self.connexion_status = connexion_status # 4 bits
self.iv = iv # 96 bits (12 bytes)
def pack(self) -> bytes:
# Pack flag and data_len as unsigned shorts (2 bytes each)
header = struct.pack('>H H', self.flag, self.data_len)
# Pack retry (1 byte) and connexion_status (4 bits in high nibble, 4 bits padding as zero)
ad_byte = (self.connexion_status & 0x0F) << 4
ad_packed = struct.pack('>B B', self.retry, ad_byte)
# Append IV (12 bytes)
return header + ad_packed + self.iv
@classmethod
def unpack(cls, data: bytes) -> 'MessageHeader':
# Expect exactly 18 bytes
flag, data_len = struct.unpack('>H H', data[:4])
retry, ad_byte = struct.unpack('>B B', data[4:6])
connexion_status = (ad_byte >> 4) & 0x0F
iv = data[6:18]
return cls(flag, data_len, retry, connexion_status, iv)
def generate_iv(initial: bool, previous_iv: bytes = None) -> bytes:
"""
Generate a 96-bit IV (12 bytes).
- If 'initial' is True, return a random IV.
- Otherwise, increment the previous IV by 1 modulo 2^96.
"""
if initial or previous_iv is None:
return os.urandom(12)
else:
iv_int = int.from_bytes(previous_iv, 'big')
iv_int = (iv_int + 1) % (1 << 96)
return iv_int.to_bytes(12, 'big')
def encrypt_message(plaintext: bytes, key: bytes, flag: int = 0xBEEF, retry: int = 0, connexion_status: int = 0) -> bytes:
"""
Encrypts a plaintext using AES-256-GCM.
- Generates a random 96-bit IV.
- Encrypts the plaintext with AESGCM.
- Builds a MessageHeader with the provided flag, the data_len (length of ciphertext excluding tag),
retry, connexion_status, and the IV.
- Returns the full encrypted message: header (18 bytes) || ciphertext || tag (16 bytes).
"""
aesgcm = AESGCM(key)
iv = generate_iv(initial=True)
# Encrypt with no associated data (you may later use the header as AD if needed)
ciphertext_with_tag = aesgcm.encrypt(iv, plaintext, None)
tag_length = 16 # default tag size
ciphertext = ciphertext_with_tag[:-tag_length]
tag = ciphertext_with_tag[-tag_length:]
data_len = len(ciphertext)
header = MessageHeader(flag=flag, data_len=data_len, retry=retry, connexion_status=connexion_status, iv=iv)
packed_header = header.pack()
return packed_header + ciphertext + tag
def decrypt_message(message: bytes, key: bytes) -> bytes:
"""
Decrypts a message that was encrypted with encrypt_message.
Expects message format: header (18 bytes) || ciphertext || tag (16 bytes).
Returns the decrypted plaintext.
"""
if len(message) < 18 + 16:
raise ValueError("Message too short.")
header_bytes = message[:18]
header = MessageHeader.unpack(header_bytes)
data_len = header.data_len
expected_len = 18 + data_len + 16
if len(message) != expected_len:
raise ValueError("Message length does not match header's data_len.")
ciphertext = message[18:18+data_len]
tag = message[18+data_len:]
ciphertext_with_tag = ciphertext + tag
aesgcm = AESGCM(key)
plaintext = aesgcm.decrypt(header.iv, ciphertext_with_tag, None)
return plaintext

View File

@ -20,8 +20,7 @@ from messages import (
)
import transmission
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes
from encryption import encrypt_message, decrypt_message
# ANSI colors
RED = "\033[91m"
@ -468,3 +467,46 @@ class IcingProtocol:
self.connections.clear()
self.inbound_messages.clear()
print(f"{RED}[STOP]{RESET} Protocol stopped.")
# New method: Send an encrypted message over the first active connection.
def send_encrypted_message(self, plaintext: str):
"""
Encrypts the provided plaintext (a UTF-8 string) using the derived HKDF key (AES-256),
and sends the encrypted message over the first active connection.
The message format is: header (18 bytes) || ciphertext || tag (16 bytes).
"""
if not self.connections:
print(f"{RED}[ERROR]{RESET} No active connections.")
return
if not self.hkdf_key:
print(f"{RED}[ERROR]{RESET} No HKDF key derived. Cannot encrypt message.")
return
key = bytes.fromhex(self.hkdf_key)
plaintext_bytes = plaintext.encode('utf-8')
encrypted = encrypt_message(plaintext_bytes, key)
# Send the encrypted message over the first connection.
self._send_packet(self.connections[0], encrypted, "ENCRYPTED_MESSAGE")
print(f"{GREEN}[SEND_ENCRYPTED]{RESET} Encrypted message sent.")
# New method: Decrypt an encrypted message provided as a hex string.
def decrypt_encrypted_message(self, hex_message: str):
"""
Decrypts an encrypted message (given as a hex string) using the HKDF key.
Returns the plaintext (UTF-8 string) and prints it.
"""
if not self.hkdf_key:
print(f"{RED}[ERROR]{RESET} No HKDF key derived. Cannot decrypt message.")
return
try:
message_bytes = bytes.fromhex(hex_message)
except Exception as e:
print(f"{RED}[ERROR]{RESET} Invalid hex input.")
return
key = bytes.fromhex(self.hkdf_key)
try:
plaintext_bytes = decrypt_message(message_bytes, key)
plaintext = plaintext_bytes.decode('utf-8')
print(f"{GREEN}[DECRYPTED]{RESET} Decrypted message: {plaintext}")
return plaintext
except Exception as e:
print(f"{RED}[ERROR]{RESET} Decryption failed: {e}")