Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
536784d
Merge pull request #16 from igmunv/dev
igmunv Jul 20, 2026
7043708
Update README.md
igmunv Aug 16, 2026
9b7c21b
Update README.md
igmunv Aug 16, 2026
0e3617c
Update README.md
igmunv Aug 16, 2026
6da6885
perf: replace polling with event-driven waits across the stack
DaYzTm Aug 22, 2026
eec5020
test: add end-to-end pipeline tests and benchmarks
DaYzTm Aug 22, 2026
0d58e37
fix: validate companion node id before using it as a file name
VyacheslavPridchin Aug 23, 2026
48aaf26
docs: add English README.md
itzdanti Aug 23, 2026
e7a687e
docs: added hyperlink to English README.md
itzdanti Aug 23, 2026
9474e95
docs: add English documentation
itzdanti Aug 23, 2026
a78024a
revert: remove "docs/" before every link to the image that broke READ…
itzdanti Aug 23, 2026
dfdbcc7
Merge pull request #19 from DaYzTm/perf/event-driven-pipeline
igmunv Aug 23, 2026
407b48d
Merge pull request #25 from itzdanti/main
igmunv Aug 23, 2026
d01e5a0
Merge pull request #21 from VyacheslavPridchin/fix/validate-companion…
igmunv Aug 23, 2026
e3e9969
revert: remove "docs/" in a link which broke the hyperlink
itzdanti Aug 23, 2026
3655bdb
fix: accept companion ECDH key only after signature verification is on
VyacheslavPridchin Aug 24, 2026
a05cebe
fix: do not send packets before the stack is initialized
VyacheslavPridchin Aug 24, 2026
d329cf3
Merge pull request #26 from itzdanti/patch-1
igmunv Aug 24, 2026
eb0c5e3
Merge pull request #20 from VyacheslavPridchin/fix/handshake-state-ma…
igmunv Aug 24, 2026
d1b1090
Merge pull request #23 from VyacheslavPridchin/fix/level-worker-crash
igmunv Aug 24, 2026
72f04a6
fix: derive AES key with HKDF and harden signature trust
Woralem Aug 24, 2026
2f64dfb
Merge pull request #28 from Woralem/fix/key-derivation-and-fingerprin…
igmunv Aug 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,14 @@ CryptoLayer использует проверенные технологии и

* **[CryptoLayer CLI](https://github.com/igmunv/cryptolayer-cli)** — официальный интерфейс для командной строки. Отлично подойдёт для работы в терминале.

### Хотите добавить свой проект?
Если вы разработали приложение с использованием **CryptoLayer**, мы с радостью добавим его в этот список!
Просто создайте Pull Request, указав название проекта, краткое описание и ссылку на репозиторий.
* **[CryptoLayer Web UI](https://github.com/DaPon4ik/cryptolayer-webui)** — web-интерфейс для защищенного обмена сообщениями в мессенджерах. Удобный и красивый интерфейс.

* **[zkgram](https://github.com/Gerate-Technik/zkgram)** — приватный клиент Telegram. Подойдёт если необходимо удобное и безопасное общение только в Telegram.

> [!NOTE]
> **Хотите добавить свой проект?**
>
> Если вы разработали приложение с использованием **CryptoLayer**, мы с радостью добавим его в этот список! Просто создайте Pull Request, указав название проекта, краткое описание и ссылку на репозиторий.

## Документация

Expand Down
95 changes: 95 additions & 0 deletions README_en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<div align="center">

<img src="docs/images/logo.svg" width="250" alt="CryptoLayer Logo">

<br>
<h3>CryptoLayer</h3>
<h6>A cryptographic layer that operates on top of existing messengers, providing end-to-end message encryption solely on the user's side</h6>

[![License](https://img.shields.io/badge/License-MIT-brightgreen?color=orange&style=flat-square)](LICENSE)
[![Contributions Welcome](https://img.shields.io/badge/Contributions-Welcome-brightgreen?style=flat-square)](CONTRIBUTING.md)
<br>
[![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54&style=flat-square)](https://www.python.org/)
<br>
<br>
[Русский](README.md) • English

</div>


## What is CryptoLayer?

**CryptoLayer** is a library that does not replace messengers but protects the content of your messages using cryptography.

<br>
<div align="center">
<img src="docs/diagrams/how-it-works-small-EN.png" width="750" alt="How CryptoLayer Works">
</div>
<br>

Simply put: for CryptoLayer, any messenger is **just an untrusted "wire"**, so all encryption and delivery guarantees happen exclusively within CryptoLayer **only on your device**.

<br>
<div align="center">
<img src="docs/diagrams/how-looks-data-transfer-EN.png" width="750" alt="How Looks Data Transfer">
</div>
<br>

## Custom Pseudo-Network Stack

The library implements its own pseudo-network stack:

<br>
<div align="center">
<img src="docs/diagrams/net-levels-EN.png" width="750" alt="Net Levels">
</div>
<br>

## Full Modularity

The main feature of CryptoLayer is its modularity! The communication channel can be anything:

- **Messengers**: Telegram, VK, Discord...
- **Network protocols**: HTTP, SSH, FTP, UDP...
- **Clouds and services**: Google Drive, Yandex Disk, YouTube comments, streaming platforms...
- **And other types**: clipboard, file system, Bluetooth...

### Anything! [Just write a module!](docs/README.md#5-модули)

The library **doesn't care** how bytes are transmitted. For it, **any** messenger, protocol, or service is simply an **untrusted "wire"**.

## Technology and Security

CryptoLayer uses proven technologies and methods to ensure the security of your data:

- **Encryption** - AES-256-GCM for content protection
- **Digital signatures and integrity verification** - ECDSA (SECP256R1 curve) for data signing
- **Key exchange** - ECDH protocol (SECP256R1 curve, X9.62 compressed point format)
- **Obfuscation** - custom byte-to-word encoding (WordCoder) to bypass basic messenger filters

## Ecosystem and Ready-made Applications

* **[CryptoLayer CLI](https://github.com/igmunv/cryptolayer-cli)** — official command-line interface. Great for terminal use.

* **[CryptoLayer Web UI](https://github.com/DaPon4ik/cryptolayer-webui)** — web interface for secure message exchange in messengers. Convenient and beautiful UI.

* **[zkgram](https://github.com/Gerate-Technik/zkgram)** — private Telegram client. Suitable if you need convenient and secure communication exclusively in Telegram.

> [!NOTE]
> **Want to add your project?**
>
> If you have developed an application using **CryptoLayer**, we will be happy to add it to this list! Simply create a Pull Request with the project name, a brief description, and a link to the repository.

## Documentation

In the [documentation](docs/README.md) you will find more information about CryptoLayer:

- **How the library works**
- **How to use it** in your code
- **Architecture** of CryptoLayer

## IMPORTANT

The user has a fundamental right to **private and secure communication**. This includes the right to **independently use cryptographic means** to protect their messages, as well as the right to confidentiality of correspondence **without unauthorized access by third parties**.

The project proceeds from the principle that secure and private communication is a **basic digital norm, not a privilege**.
126 changes: 126 additions & 0 deletions bench/bench.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
"""Benchmark the CryptoLayer send/receive transform pipeline.

Measures every stage that touches a message on its way out and back in,
excluding threading/polling overhead, so the pure CPU cost is visible.

Run: python3 bench/bench.py
"""
import os
import statistics
import sys
import time

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))

import brotli
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

import config
from levels.packet import ApplicationPacket, DataTypes, PackTypes, TextMessagePacket, TransportPacket
from wordcoder import WordCoder

# 256 distinct <=10-char words, same shape as the real dictionary repo.
SYLL_A = ["ba", "ve", "gi", "do", "zhu", "ki", "la", "mo", "ne", "pu", "ra", "so", "tu", "fi", "ha", "che"]
SYLL_B = ["lom", "ves", "gor", "dym", "zhar", "kit", "lug", "mox", "nos", "puh", "rov", "sud", "tir", "fon", "hor", "chan"]
WORDCODER_DICT = {
f"{a * 16 + b:02x}": SYLL_A[a] + SYLL_B[b]
for a in range(16)
for b in range(16)
}

PAYLOADS = {
"short (32B)": b"x" * 32,
"chat (256B)": ("Wake me up when september ends. " * 8).encode(),
"long (4KB)": (os.urandom(16).hex() * 256).encode()[:4096],
}


def timeit(fn, *, min_rounds=50, min_seconds=0.25):
"""Return (median_seconds, rounds). Warms up, then loops until stable."""
for _ in range(3):
fn()
samples = []
deadline = time.perf_counter() + min_seconds
while len(samples) < min_rounds or time.perf_counter() < deadline:
t0 = time.perf_counter()
fn()
samples.append(time.perf_counter() - t0)
return statistics.median(samples), len(samples)


def fmt(seconds):
if seconds >= 1:
return f"{seconds:8.3f} s "
if seconds >= 1e-3:
return f"{seconds * 1e3:8.3f} ms"
return f"{seconds * 1e6:8.3f} us"


def main():
wc = WordCoder(WORDCODER_DICT)
aes_key = os.urandom(32)
aesgcm = AESGCM(aes_key)
sign_key = ec.generate_private_key(ec.SECP256R1())
verify_key = sign_key.public_key()
chunk_size = config.CHUNK_SIZE

print(f"CHUNK_SIZE={chunk_size} COMPRESS_QUALITY={config.COMPRESS_QUALITY}")

for name, raw in PAYLOADS.items():
# --- stage inputs, mirroring the real pipeline ---
app_bytes = ApplicationPacket(
PackTypes.COMMUNIC.value,
DataTypes.TEXT.value,
TextMessagePacket(int(time.time()), raw).to_bytes(),
).to_bytes()

compressed = brotli.compress(app_bytes, quality=config.COMPRESS_QUALITY)
nonce = os.urandom(12)
encrypted = nonce + aesgcm.encrypt(nonce, compressed, associated_data=None)
chunks = [encrypted[i:i + chunk_size] for i in range(0, len(encrypted), chunk_size)]
n_chunks = len(chunks)
transport_packets = [
TransportPacket(0x0, 0, n_chunks, i, int(time.time()), c).to_bytes()
for i, c in enumerate(chunks)
]
signature = sign_key.sign(transport_packets[0], ec.ECDSA(hashes.SHA256()))
signed = len(signature).to_bytes(1, "big") + signature + transport_packets[0]
wire = " ".join(wc.encode(signed))

print(f"\n=== {name} ===")
print(f" app packet {len(app_bytes)}B -> brotli {len(compressed)}B "
f"-> +aesgcm {len(encrypted)}B -> {n_chunks} chunk(s) "
f"-> wire {len(wire)} chars ({len(wire) / len(raw):.1f}x expansion)")

stages = [
# (label, callable, how many times it runs per message)
("brotli.compress", lambda: brotli.compress(app_bytes, quality=config.COMPRESS_QUALITY), 1),
("brotli.decompress", lambda: brotli.decompress(compressed), 1),
("aesgcm.encrypt", lambda: aesgcm.encrypt(nonce, compressed, associated_data=None), 1),
("aesgcm.decrypt", lambda: aesgcm.decrypt(nonce, encrypted[12:], associated_data=None), 1),
("ecdsa.sign", lambda: sign_key.sign(transport_packets[0], ec.ECDSA(hashes.SHA256())), n_chunks),
("ecdsa.verify", lambda: verify_key.verify(signature, transport_packets[0], ec.ECDSA(hashes.SHA256())), n_chunks),
("wordcoder.encode", lambda: " ".join(wc.encode(signed)), n_chunks),
("wordcoder.decode", lambda: wc.decode(wire.split(" ")), n_chunks),
]

total_send = 0.0
total_recv = 0.0
print(f" {'stage':<20} {'median':>12} {'xN':>4} {'per message':>13}")
for label, fn, times in stages:
median, _ = timeit(fn)
per_msg = median * times
if "compress" in label and "de" not in label or label in ("aesgcm.encrypt", "ecdsa.sign", "wordcoder.encode"):
total_send += per_msg
else:
total_recv += per_msg
print(f" {label:<20} {fmt(median)} x{times:<3} {fmt(per_msg)}")
print(f" {'TOTAL send':<20} {'':>12} {fmt(total_send)}")
print(f" {'TOTAL recv':<20} {'':>12} {fmt(total_recv)}")
print(f" {'THROUGHPUT send':<20} {'':>12} {len(raw) / total_send / 1024:8.1f} KiB/s")


if __name__ == "__main__":
main()
Loading
Loading