This repository is StormByte Crypto: hash, compress, encrypt, sign and key agreement for the StormByte C++ suite.
It depends on StormByte Base and StormByte Buffer. Public headers live under StormByte/crypto/. Crypto++ stays in the private tree: installed headers never mention CryptoPP::. With a static Crypto++ link, consumers do not install it.
The suite is split on purpose. Base, Buffer, Config, Database, Logger, Multimedia, Network and System are other repositories. This one does not implement them.
- Hasher — SHA-256, SHA-512, SHA3-256, SHA3-512, BLAKE2b, BLAKE2s. One-shot hex digest or a
Buffer::Consumerthat yields the digest when the source closes. - Compressor — Zlib, Gzip, BZip2. Same block / stream contract as the rest of the module.
- Symmetric crypter — AES CBC, AES-GCM, ChaCha20-Poly1305, Camellia, Serpent, Twofish. Keys from
Passwordvia PBKDF2-HMAC-SHA256 (600 000 iterations). Authenticated modes fail closed on a bad tag or a wrong password. - Asymmetric crypter — RSA OAEP-SHA and ECC ECIES.
Strategy::Nativeis one PK transform per blob.Strategy::Hybridwraps a random AES-256-GCM session key. Decrypt auto-detects the envelope. - Signer — DSA, RSA PKCS#1 v1.5 + SHA-256, ECDSA, Ed25519. Block and streaming sign / verify.
- Secret — ECDH on secp256r1 / secp384r1 / secp521r1, and X25519. The shared secret is a
Password, not astd::string. - KeyPair — Generate, persist PEM or DER, optional PKCS#8 (PBES2 + PBKDF2 + AES-256-CBC, OpenSSL-compatible). Public key travels as Base64 SPKI; private key stays in a
Password. - Password / Vault — reference-counted wiped buffer; named store. Last owner zeros the bytes. Vault is movable, not copyable.
- Buffer-first I/O —
std::span<const std::byte>→Buffer::WriteOnlyfor blocks;Buffer::Consumerin / out for pipelines (Network, Multimedia).
| Module | Role | API |
|---|---|---|
| Base | Exceptions, Expected, serialization, strings, UUID, concepts | /StormByte |
| Buffer | FIFO, SharedFIFO, Ring, Producer/Consumer and multi-stage pipelines | /StormByte-Buffer |
| Config | Human-readable text and versioned binary documents (groups, lists, raw bytes) | /StormByte-Config |
| Crypto | This repository | /StormByte-Crypto |
| Database | One API over SQLite, PostgreSQL and MariaDB | /StormByte-Database |
| Logger | Stream logger with levels, headers, human-readable sizes and redaction (ThreadedLog) |
/StormByte-Logger |
| Multimedia | Decode, encode and containers without raw FFmpeg types; codecs enabled only if present | /StormByte-Multimedia |
| Network | Framed packets, Client/Server, IPv4/IPv6 TCP and Buffer pipelines (compress/encrypt) | /StormByte-Network |
| System | Processes, pipes and environment variables across Linux, Windows and macOS | /StormByte-System |
Needs a C++26 compiler, CMake 3.28 or newer, StormByte Base ≥ 1.1.0 and StormByte Buffer ≥ 1.1.0. Crypto++ and libbzip2 are build dependencies. Prefer a static Crypto++ link when you redistribute.
git clone --recursive https://github.com/StormBytePP/StormByte-Crypto.git
cd StormByte-Crypto
cmake -S . -B build
cmake --build buildHeaders are #include <StormByte/crypto/….hxx>. Namespace root is StormByte::Crypto.
Nothing in the public tree includes Crypto++. Private headers are not installed.
auto hasher = Hasher::Create(Hasher::Type::SHA256);
auto zip = Compressor::Create(Compressor::Type::Zlib, 6);
auto aes = Crypter::Create(Crypter::Type::AES_GCM, password);
auto rsaKp = KeyPair::RSA::Generate(2048);
auto rsa = Crypter::Create(Crypter::Type::RSA, rsaKp); // Hybrid by default
auto signer = Signer::Create(Signer::Type::ECDSA, ecdsaKp);
auto ecdh = Secret::Create(Secret::Type::ECDH, ecdhKp);Concrete types (Crypter::AES_GCM, KeyPair::X25519, Signer::ED25519, …) construct the same way without going through Create.
Password is the only public container for secret bytes (passphrases, private key DER, shared secrets). Copies share the buffer; the last owner wipes it.
#include <StormByte/crypto/password.hxx>
#include <StormByte/crypto/vault.hxx>
#include <StormByte/crypto/crypter/generic.hxx>
using namespace StormByte::Crypto;
Password db("s3cret-from-env");
Vault vault;
vault.Store("database", db);
vault.Store("api", Password("token-xyz"));
if (auto p = vault.Get("database")) {
auto aes = Crypter::Create(Crypter::Type::AES_GCM, *p);
}
if (*vault.Get("database") == db)
; // constant-time compare of the bytes
vault.Remove("api");
vault.Clear();Vault is movable, not copyable. A move leaves the source empty.
#include <StormByte/crypto/hasher/generic.hxx>
#include <StormByte/crypto/compressor/generic.hxx>
#include <StormByte/buffer/fifo.hxx>
#include <StormByte/buffer/producer.hxx>
using namespace StormByte::Crypto;
auto sha = Hasher::Create(Hasher::Type::SHA256);
auto zip = Compressor::Create(Compressor::Type::Zlib, 6);
StormByte::Buffer::FIFO digest, packed;
const char msg[] = "payload";
const auto span = std::span<const std::byte>(
reinterpret_cast<const std::byte*>(msg), sizeof(msg) - 1);
sha->Hash(span, digest); // hex digest
zip->Compress(span, packed);
StormByte::Buffer::Producer prod;
prod.Write(msg);
prod.Close();
auto hashed = sha->Hash(prod.Consumer()); // Consumer → hexPassword → random salt + PBKDF2-HMAC-SHA256 → key. AES-GCM and ChaCha20-Poly1305 authenticate; a wrong password or a flipped bit returns false.
#include <StormByte/crypto/crypter/symmetric/aes_gcm.hxx>
#include <StormByte/crypto/password.hxx>
#include <StormByte/buffer/fifo.hxx>
#include <StormByte/buffer/producer.hxx>
using namespace StormByte::Crypto;
Password password("SecurePassword123!");
Crypter::AES_GCM gcm(password);
StormByte::Buffer::FIFO encrypted, decrypted;
const char msg[] = "authenticated payload";
const auto span = std::span<const std::byte>(
reinterpret_cast<const std::byte*>(msg), sizeof(msg) - 1);
gcm.Encrypt(span, encrypted);
gcm.Decrypt(
std::span<const std::byte>(encrypted.Data().data(), encrypted.Data().size()),
decrypted);
StormByte::Buffer::Producer prod;
prod.Write(msg);
prod.Close();
auto cipher = gcm.Encrypt(prod.Consumer());
auto plain = gcm.Decrypt(cipher);CBC siblings (AES, Camellia, Serpent, Twofish) use the same Encrypt / Decrypt names.
Native is one PK operation per blob (small messages). Hybrid is a random AES-256-GCM key wrapped with the recipient public key (anything that would be painful as raw RSA/ECIES). Decrypt reads the header and picks the path.
#include <StormByte/crypto/keypair/rsa.hxx>
#include <StormByte/crypto/crypter/asymmetric/rsa.hxx>
#include <StormByte/buffer/fifo.hxx>
using namespace StormByte::Crypto;
auto kp = KeyPair::RSA::Generate(2048);
Crypter::RSA hybrid(kp); // Strategy::Hybrid
Crypter::RSA native(kp, Crypter::Asymmetric::Strategy::Native);
StormByte::Buffer::FIFO out;
hybrid.Encrypt(span, out);
hybrid.Decrypt(
std::span<const std::byte>(out.Data().data(), out.Data().size()),
out);ECC (Crypter::ECC + KeyPair::ECC) is the same API.
| Format | Meaning |
|---|---|
PEM |
OpenSSL text (BEGIN / Base64). Default. |
DER |
Raw ASN.1. Same family as many .cer / .crt blobs. |
#include <StormByte/crypto/keypair/rsa.hxx>
#include <StormByte/crypto/password.hxx>
using namespace StormByte::Crypto;
auto kp = KeyPair::RSA::Generate(2048);
kp->Save("/tmp/keys", "app", KeyPair::StorageFormat::PEM);
Password wrap("disk-secret");
kp->Save("/tmp/keys", "app-enc", KeyPair::StorageFormat::PEM, wrap);
auto loaded = KeyPair::Load("/tmp/keys/app.pub.pem", "/tmp/keys/app.pem");
auto enc = KeyPair::Load("/tmp/keys/app-enc.pub.pem", "/tmp/keys/app-enc.pem", wrap);Wrong or missing wrap password fails closed (nullptr). Type comes from the OID (RSA, DSA, EC, Ed25519, X25519). Generate → Save → Load stays usable for encrypt, sign and share. X25519 also understands raw 32-byte library form.
#include <StormByte/crypto/keypair/ed25519.hxx>
#include <StormByte/crypto/signer/generic.hxx>
#include <StormByte/buffer/fifo.hxx>
using namespace StormByte::Crypto;
auto kp = KeyPair::ED25519::Generate();
auto signer = Signer::Create(Signer::Type::ED25519, kp);
StormByte::Buffer::FIFO sig;
signer->Sign(span, sig);
bool ok = signer->Verify(span, std::string(
reinterpret_cast<const char*>(sig.Data().data()), sig.Data().size()));Streaming: signer->Sign(consumer) / signer->Verify(consumer, signature).
#include <StormByte/crypto/keypair/x25519.hxx>
#include <StormByte/crypto/secret/x25519.hxx>
using namespace StormByte::Crypto;
auto alice = KeyPair::X25519::Generate();
auto bob = KeyPair::X25519::Generate();
auto secret = Secret::Create(Secret::Type::X25519, alice);
auto shared = secret->Share(bob->PublicKey()); // Expected<Password>ECDH is the same with KeyPair::ECDH::Generate(256|384|521) and Secret::Type::ECDH.
- Decompression of untrusted input is not size-bounded. Like the underlying zlib/libbzip2,
Compressordecompresses as much as the stream decodes to; a small malicious input can expand to a very large output ("decompression bomb"). If you decompress data from an untrusted source, bound it yourself: check the expected/maximum size before decompressing, or stop draining the streamingConsumeronce your own limit is hit. - Private key files written by
KeyPair::Save/SavePrivateare created owner-only (0600on POSIX) and refuse to write through a pre-existing symlink at the destination path. Public key files are unaffected by either restriction.
Issues only on this repository. Fork and open a pull request against master.
GNU Lesser General Public License version 3 or later. See LICENSE and https://www.gnu.org/licenses/lgpl-3.0.html.