mtp/crypto
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Alois 89a20044a5
Some checks failed
CI / rustfmt (push) Successful in 17s
CI / clippy (push) Failing after 1m16s
CI / wasm build (push) Successful in 1m17s
CI / example (push) Successful in 1m29s
CI / test (push) Successful in 1m49s
CI / duplicate code (push) Successful in 12s
CI / web client (push) Failing after 27s
CI / cargo-machete (push) Successful in 1m10s
CI / cargo-deny (push) Failing after 2m20s
(feat): rename example-usage to just example
(feat): add the example's web-client dist folder to a gitignore
(fix): format issues
(fix): a lot of duplicate code
2026-06-27 03:20:25 +02:00
..
src (feat): rename example-usage to just example 2026-06-27 03:20:25 +02:00
Cargo.lock Gitignores 2026-06-27 02:00:42 +02:00
Cargo.toml (feat): add package.json for easy wasm installs 2026-06-27 02:32:43 +02:00
README.md Host & Client force randomness on each other. 2026-06-26 17:08:48 +02:00

mtp-crypto

Cryptographic primitives for the MTP protocol. Classical and post-quantum.

Features

Feature Primitives Status
default XChaCha20-Poly1305, Ed25519, HKDF-SHA-256, SHA-256 Classical
full default + AES-256-GCM Classical
pqc ML-KEM-768+X25519 hybrid KEM, ML-DSA-65 Post-quantum

AEAD

XChaCha20-Poly1305 (default) and AES-256-GCM (full feature). Nonce is prepended to ciphertext.

use mtp_crypto::{ChaCha20Poly1305, AeadEncrypt, AeadDecrypt};

let cipher = ChaCha20Poly1305::new([0u8; 32]);
let ct = cipher.encrypt(b"hello", b"aad")?;
let pt = cipher.decrypt(&ct, b"aad")?;

Signatures

Ed25519

use mtp_crypto::{Ed25519Signer, SignatureScheme};

let (signer, sk, pk) = Ed25519Signer::generate();
let sig = signer.sign(b"message")?;
signer.verify(b"message", &sig)?;

ML-DSA-65

use mtp_crypto::{MlDsaSigner, SignatureScheme};

let (signer, sk, pk) = MlDsaSigner::generate();
let sig = signer.sign(b"message")?;
signer.verify(b"message", &sig)?;

// Load from stored bytes
let signer = MlDsaSigner::new(&sk, &pk)?;

Dual signatures

use mtp_crypto::{sign_dual, DualSignature, Ed25519Signer, MlDsaSigner};

let (ed_signer, _, _) = Ed25519Signer::generate();
let (ml_signer, _, _) = MlDsaSigner::generate();
let dual = sign_dual(ed_signer.signing_key(), ml_signer.signing_key(), b"msg");
dual.verify(ed_signer.verifying_key(), ml_signer.verifying_key(), b"msg")?;

Hybrid KEM

X25519 + ML-KEM-768. 64-byte shared secret. Feed into HKDF before use.

use mtp_crypto::HybridKem;

let (sk, pk) = HybridKem::generate_keypair();
let enc = HybridKem::encapsulate(&pk)?;
let ss = HybridKem::decapsulate(&sk, &enc.ciphertext)?;
assert_eq!(enc.shared_secret, ss);

KDF

use mtp_crypto::{hkdf_expand, derive_encryption_key};

let key = derive_encryption_key(b"ikm", b"salt", b"context")?;

Hashing

use mtp_crypto::{sha256, sha256_double};

let h = sha256(b"data");
let h2 = sha256_double(b"data");

Key types

Type Secret Zeroized
EncryptionPrivateKey KEM/ECDH secret Yes
EncryptionPublicKey KEM/ECDH public No
SignaturePrivateKey Classical signing key Yes
SignaturePublicKey Classical verifying key No
KemPrivateKey Hybrid KEM secret Yes
KemPublicKey Hybrid KEM public No
SignaturePqPrivateKey PQC signing key Yes
SignaturePqPublicKey PQC verifying key No

KeyGroup holds classical keys; Keyring holds all six (hybrid KEM + PQ sig + classical sig).

Feature flags

[dependencies]
mtp-crypto = { path = "../crypto" }                          # classical
mtp-crypto = { path = "../crypto", features = ["pqc"] }       # post-quantum
mtp-crypto = { path = "../crypto", features = ["full", "pqc"] } # all