# SMP25519 | Secure Messaging Protocol 25519 | Python
![SMP25519 Flow Chart](./svg/smp25519_flow_chart.svg)
## Overview
SMP25519 is designed to facilitate secure communication using the X25519 key exchange, BLAKE3 hashing, and ChaCha20 encryption. It provides a straightforward interface for generating secure identities, creating handshake messages, deriving shared secrets, and encrypting/decrypting data.
## Installation
```
pip install smp25519
```
## Dependencies
To use SMP25519, install the required dependencies:
```
pip install cryptography blake3 pycryptodomex
```
## License
This package is distributed under the [Unlicense](https://choosealicense.com/licenses/unlicense/).
## Contact
For support or inquiries, contact truebreaker@proton.me.
# Examples
## Client
```python
import socket
import smp25519
import base64
def main() -> None:
"""
Secure UDP client example using the smp25519 package.
This script demonstrates how to establish a secure communication channel with a server using key exchange and encryption.
"""
# Step 1: Generate client identity (private key, public key, and connection ID).
private_key, public_key, connection_id = smp25519.generate_identity()
# Step 2 (RECOMMENDED): Define the server's known public key (Base64 encoded).
known_server_public_key = base64.b64decode("Vh4DBTYyDbwTqg1eZzTnuTxThscIoNQgLpxgsBCOFCU=".encode())
# Step 3: Create a UDP socket.
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
dest_addr = ("localhost", 12000) # Server address and port.
print(f"Secure UDP Client: Attempting connection to {dest_addr}")
# Step 4: Send handshake message containing the client's public key.
sock.sendto(smp25519.create_handshake_message(public_key), dest_addr)
# Step 5: Receive and validate handshake response from the server.
data, addr = sock.recvfrom(1024)
if smp25519.is_handshake_message(data) == False:
print("Error: Handshake failed. Invalid response received.")
return
# Extract the server's public key from the handshake message.
server_public_key = smp25519.extract_public_key_from_handshake(data)
# (RECOMMENDED) Verify the server's public key.
if server_public_key != known_server_public_key:
print("Error: Known server public key mismatch. Aborting connection.")
return
# Step 6: Derive the shared secret using the server's public key and a salt.
# shared_secret = smp25519.derive_shared_secret(private_key, server_public_key, b"examplesalt")
shared_secret = smp25519.derive_shared_secret(private_key, server_public_key)
# Step 7: Exchange encrypted messages with the server.
while True:
# Input message from the user.
message = input("Enter a message to send (or press Enter to retry): ").strip()
if len(message) == 0:
continue
# Encrypt and send the message.
encrypted_message = smp25519.encrypt_and_send_data(connection_id, message.encode(), shared_secret)
sock.sendto(encrypted_message, dest_addr)
# Receive and decrypt the server's response.
data, addr = sock.recvfrom(1024)
decrypted_message = smp25519.decrypt_received_data(data, shared_secret)
print(f"Server response from {addr}: {decrypted_message.decode()}")
if __name__ == "__main__":
main()
```
## Server
```python
import socket
import smp25519
import base64
def main() -> None:
"""
Secure UDP server example using the smp25519 package.
This script demonstrates how to establish a secure communication channel with a single
client at a time using key exchange and encryption.
"""
# Step 1: Generate the server's identity.
# private_key, public_key, connection_id = smp25519.generate_identity()
# Or use a pre-existing private key (Base64 encoded) and derive the public key.
private_key = base64.b64decode("4Pe2QvF6zk41OWkMTqVR8e9nvwhbOEaDRti6oykaG18=".encode())
public_key = smp25519.get_public_key_from_private(private_key)
print(f"Server public key (Base64): {base64.b64encode(public_key).decode()}")
# Step 2: Set up the UDP socket and bind to a port.
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
dest_addr = ("localhost", 12000)
sock.bind(dest_addr)
print(f"Secure UDP Server: Listening on {dest_addr}")
# Variables to store client-specific connection data.
client_connection_id: bytes = None
client_shared_secret: bytes = None
# Step 3: Main server loop.
while True:
# Receive data from a client.
data, addr = sock.recvfrom(1024)
print(f"Connection from {addr}")
# Step 4: Handle handshake messages.
if smp25519.is_handshake_message(data) == True:
print(f"Handshake received from {addr}")
# Extract the client's public key and generate a connection ID.
client_public_key = smp25519.extract_public_key_from_handshake(data)
client_connection_id = smp25519.generate_connection_id_from_public_key(client_public_key)
# Derive a shared secret using the client's public key and a salt.
# client_shared_secret = smp25519.derive_shared_secret(private_key, client_public_key, b"examplesalt")
client_shared_secret = smp25519.derive_shared_secret(private_key, client_public_key)
# Respond with the server's handshake message.
handshake = smp25519.create_handshake_message(public_key)
sock.sendto(handshake, addr)
print("Handshake completed.")
continue
# Step 5: Handle encrypted messages.
if smp25519.is_valid_data(data) == True:
# Verify the connection ID matches the client.
if smp25519.extract_connection_id_from_data(data) != client_connection_id:
print(f"Error: Unknown client ID from {addr}. Ignoring message.")
continue
# Decrypt the received message.
decrypted_message = smp25519.decrypt_received_data(data, client_shared_secret)
print(f"Message from {addr}: {decrypted_message.decode()}")
# Send an encrypted response back to the client.
response_message = "Hello from Server!"
encrypted_response = smp25519.encrypt_and_send_data(client_connection_id, response_message.encode(), client_shared_secret)
sock.sendto(encrypted_response, addr)
print("Response sent.")
continue
# Step 6: Handle unrecognized data.
print(f"Error: Received unknown data from {addr}")
if __name__ == "__main__":
main()
```
Raw data
{
"_id": null,
"home_page": "https://github.com/979st/smp25519-python",
"name": "smp25519",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": "python, sockets, udp, secure, x25519, blake3, chacha20, protocol, messaging",
"author": null,
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/4d/d3/68c83bb2ef43d494bde766ab2452220ca6153474e21069360c372b633b23/smp25519-1.0.0.tar.gz",
"platform": null,
"description": "# SMP25519 | Secure Messaging Protocol 25519 | Python\r\n![SMP25519 Flow Chart](./svg/smp25519_flow_chart.svg)\r\n## Overview\r\nSMP25519 is designed to facilitate secure communication using the X25519 key exchange, BLAKE3 hashing, and ChaCha20 encryption. It provides a straightforward interface for generating secure identities, creating handshake messages, deriving shared secrets, and encrypting/decrypting data.\r\n## Installation\r\n```\r\npip install smp25519\r\n```\r\n## Dependencies\r\nTo use SMP25519, install the required dependencies:\r\n```\r\npip install cryptography blake3 pycryptodomex\r\n```\r\n## License\r\nThis package is distributed under the [Unlicense](https://choosealicense.com/licenses/unlicense/).\r\n## Contact\r\nFor support or inquiries, contact truebreaker@proton.me.\r\n# Examples\r\n## Client\r\n```python\r\nimport socket\r\nimport smp25519\r\nimport base64\r\n\r\ndef main() -> None:\r\n \"\"\"\r\n Secure UDP client example using the smp25519 package.\r\n This script demonstrates how to establish a secure communication channel with a server using key exchange and encryption.\r\n \"\"\"\r\n # Step 1: Generate client identity (private key, public key, and connection ID).\r\n private_key, public_key, connection_id = smp25519.generate_identity()\r\n\r\n # Step 2 (RECOMMENDED): Define the server's known public key (Base64 encoded).\r\n known_server_public_key = base64.b64decode(\"Vh4DBTYyDbwTqg1eZzTnuTxThscIoNQgLpxgsBCOFCU=\".encode())\r\n\r\n # Step 3: Create a UDP socket.\r\n sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\r\n dest_addr = (\"localhost\", 12000) # Server address and port.\r\n\r\n print(f\"Secure UDP Client: Attempting connection to {dest_addr}\")\r\n\r\n # Step 4: Send handshake message containing the client's public key.\r\n sock.sendto(smp25519.create_handshake_message(public_key), dest_addr)\r\n\r\n # Step 5: Receive and validate handshake response from the server.\r\n data, addr = sock.recvfrom(1024)\r\n if smp25519.is_handshake_message(data) == False:\r\n print(\"Error: Handshake failed. Invalid response received.\")\r\n return\r\n \r\n # Extract the server's public key from the handshake message.\r\n server_public_key = smp25519.extract_public_key_from_handshake(data)\r\n\r\n # (RECOMMENDED) Verify the server's public key.\r\n if server_public_key != known_server_public_key:\r\n print(\"Error: Known server public key mismatch. Aborting connection.\")\r\n return\r\n\r\n # Step 6: Derive the shared secret using the server's public key and a salt.\r\n # shared_secret = smp25519.derive_shared_secret(private_key, server_public_key, b\"examplesalt\")\r\n shared_secret = smp25519.derive_shared_secret(private_key, server_public_key)\r\n\r\n # Step 7: Exchange encrypted messages with the server.\r\n while True:\r\n # Input message from the user.\r\n message = input(\"Enter a message to send (or press Enter to retry): \").strip()\r\n if len(message) == 0:\r\n continue\r\n \r\n # Encrypt and send the message.\r\n encrypted_message = smp25519.encrypt_and_send_data(connection_id, message.encode(), shared_secret)\r\n sock.sendto(encrypted_message, dest_addr)\r\n\r\n # Receive and decrypt the server's response.\r\n data, addr = sock.recvfrom(1024)\r\n decrypted_message = smp25519.decrypt_received_data(data, shared_secret)\r\n print(f\"Server response from {addr}: {decrypted_message.decode()}\")\r\n\r\nif __name__ == \"__main__\":\r\n main()\r\n```\r\n## Server\r\n```python\r\nimport socket\r\nimport smp25519\r\nimport base64\r\n\r\ndef main() -> None:\r\n \"\"\"\r\n Secure UDP server example using the smp25519 package.\r\n This script demonstrates how to establish a secure communication channel with a single\r\n client at a time using key exchange and encryption.\r\n \"\"\"\r\n # Step 1: Generate the server's identity.\r\n # private_key, public_key, connection_id = smp25519.generate_identity()\r\n\r\n # Or use a pre-existing private key (Base64 encoded) and derive the public key.\r\n private_key = base64.b64decode(\"4Pe2QvF6zk41OWkMTqVR8e9nvwhbOEaDRti6oykaG18=\".encode())\r\n public_key = smp25519.get_public_key_from_private(private_key)\r\n print(f\"Server public key (Base64): {base64.b64encode(public_key).decode()}\")\r\n\r\n # Step 2: Set up the UDP socket and bind to a port.\r\n sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\r\n dest_addr = (\"localhost\", 12000)\r\n sock.bind(dest_addr)\r\n print(f\"Secure UDP Server: Listening on {dest_addr}\")\r\n\r\n # Variables to store client-specific connection data.\r\n client_connection_id: bytes = None\r\n client_shared_secret: bytes = None\r\n\r\n # Step 3: Main server loop.\r\n while True:\r\n # Receive data from a client.\r\n data, addr = sock.recvfrom(1024)\r\n print(f\"Connection from {addr}\")\r\n\r\n # Step 4: Handle handshake messages.\r\n if smp25519.is_handshake_message(data) == True:\r\n print(f\"Handshake received from {addr}\")\r\n\r\n # Extract the client's public key and generate a connection ID.\r\n client_public_key = smp25519.extract_public_key_from_handshake(data)\r\n client_connection_id = smp25519.generate_connection_id_from_public_key(client_public_key)\r\n\r\n # Derive a shared secret using the client's public key and a salt.\r\n # client_shared_secret = smp25519.derive_shared_secret(private_key, client_public_key, b\"examplesalt\")\r\n client_shared_secret = smp25519.derive_shared_secret(private_key, client_public_key)\r\n\r\n # Respond with the server's handshake message.\r\n handshake = smp25519.create_handshake_message(public_key)\r\n sock.sendto(handshake, addr)\r\n print(\"Handshake completed.\")\r\n continue\r\n \r\n # Step 5: Handle encrypted messages.\r\n if smp25519.is_valid_data(data) == True:\r\n # Verify the connection ID matches the client.\r\n if smp25519.extract_connection_id_from_data(data) != client_connection_id:\r\n print(f\"Error: Unknown client ID from {addr}. Ignoring message.\")\r\n continue\r\n \r\n # Decrypt the received message.\r\n decrypted_message = smp25519.decrypt_received_data(data, client_shared_secret)\r\n print(f\"Message from {addr}: {decrypted_message.decode()}\")\r\n\r\n # Send an encrypted response back to the client.\r\n response_message = \"Hello from Server!\"\r\n encrypted_response = smp25519.encrypt_and_send_data(client_connection_id, response_message.encode(), client_shared_secret)\r\n sock.sendto(encrypted_response, addr)\r\n print(\"Response sent.\")\r\n continue\r\n \r\n # Step 6: Handle unrecognized data.\r\n print(f\"Error: Received unknown data from {addr}\")\r\n\r\nif __name__ == \"__main__\":\r\n main()\r\n```\r\n",
"bugtrack_url": null,
"license": "Unlicense",
"summary": "Secure Messaging Protocol 25519",
"version": "1.0.0",
"project_urls": {
"Homepage": "https://github.com/979st/smp25519-python"
},
"split_keywords": [
"python",
" sockets",
" udp",
" secure",
" x25519",
" blake3",
" chacha20",
" protocol",
" messaging"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "eec1198a516d7c97832d00bc3ccf1e61926be329f24537c13dfa16b5e47b73e5",
"md5": "576bd7713f49c352eb28f37d208f5ebb",
"sha256": "de5a520b36605026034c6213a1915aff407350837f999751b22ce620a2f31596"
},
"downloads": -1,
"filename": "smp25519-1.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "576bd7713f49c352eb28f37d208f5ebb",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 6525,
"upload_time": "2025-01-18T21:07:14",
"upload_time_iso_8601": "2025-01-18T21:07:14.195501Z",
"url": "https://files.pythonhosted.org/packages/ee/c1/198a516d7c97832d00bc3ccf1e61926be329f24537c13dfa16b5e47b73e5/smp25519-1.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "4dd368c83bb2ef43d494bde766ab2452220ca6153474e21069360c372b633b23",
"md5": "2e360c644776b75a65776add4ef61e50",
"sha256": "6e455d1b93bd7b4ff25ca72f728943a025ed3768cc88016c88a30a782b8b156b"
},
"downloads": -1,
"filename": "smp25519-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "2e360c644776b75a65776add4ef61e50",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 6400,
"upload_time": "2025-01-18T21:07:15",
"upload_time_iso_8601": "2025-01-18T21:07:15.959947Z",
"url": "https://files.pythonhosted.org/packages/4d/d3/68c83bb2ef43d494bde766ab2452220ca6153474e21069360c372b633b23/smp25519-1.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-01-18 21:07:15",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "979st",
"github_project": "smp25519-python",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "smp25519"
}