Version 1.1.0
[Summary]
StormByte Crypto is the cryptography module of the StormByte C++ suite.
It depends on StormByte Base and StormByte Buffer. This repository is not Base, Buffer, Config, Database, Logger, Multimedia, Network or System.
Public headers under StormByte/crypto/ cover Hasher, Compressor, Crypter (symmetric and asymmetric), Signer, Secret, KeyPair, Password and Vault. Crypto++ never leaves the private tree.
If you landed here from a release link and have not read the tree:
- What this module is, how to build it, and short examples: README.md
- License: GNU Lesser General Public License version 3 or later, LICENSE
Changed
- Exception hierarchy ported to
StormByte::Component:Crypto::Exceptionnames itself"Crypto", and each per-component exception (CompressorException,CrypterException,HasherException,KeyPairException,SecretException,SignerException,VaultException) combines its own name with the parent's through its constructor instead of manual string concatenation. Removed the now-unneeded workaround for MSVC constructor-inheritance ambiguity. - Bumped the StormByte Buffer dependency to 1.1.0.
Added
VaultException:Vault::Geton a missing entry now returns a dedicated exception instead of the genericException.- Header-only
StormByte::Type::ByteInputRangeoverloads for block hashing, compression, encryption, signing and signature verification. They accept byte-convertible input ranges such asstd::string_view,std::vector<uint8_t>andstd::span, then delegate to the existing byte-span APIs without changing their ABI.
Fixed
- Security hardening of
KeyPairprivate-key handling, found and closed during a full pre-release audit:- Private-key material (PKCS#8 DER, PBES2 plaintext/ciphertext) was never actually wiped from memory. The wipe helper constructed a new
CryptoPP::SecByteBlockcopy from the buffer's pointer and zeroed that copy instead of the original —CryptoPP::SecBlock's(pointer, length)constructor always allocates and copies, it never wraps existing storage. Added a directSecureWipeoverload forstd::vector<unsigned char>and wipe the original buffers (andstd::stringplaintext buffers) in place. - The shared
CryptoPP::AutoSeededRandomPoolused for salt/IV/key generation was a single process-wide instance accessed without synchronization.AutoSeededRandomPoolis not safe for concurrent use, and the streaming encrypt/decrypt paths each spawn their own detached worker thread, so two concurrent streaming operations raced on the RNG's internal state. Made itthread_localinstead — confirmed race-free with ThreadSanitizer (fully-instrumentedWITH_CRYPTOPP=BUNDLEDbuild; theSYSTEMbuild previously produced ABI-boundary false positives). - Private key files (
KeyPair::Save/SavePrivate, encrypted or not, PEM or DER) were created with the OS-default file permissions, potentially group/world-readable depending on umask. They are now restricted to owner read/write (0600) right after writing. Public key files are unaffected. Best-effort on filesystems/platforms without POSIX permission bits. WriteFileBytes(used by everyKeyPair::Save/SavePublic/SavePrivatepath) refuses to write through a pre-existing symlink at the destination path, closing a local TOCTOU attack where a symlink planted at the target filename would redirect the write to an arbitrary file.
- Private-key material (PKCS#8 DER, PBES2 plaintext/ciphertext) was never actually wiped from memory. The wipe helper constructed a new
- CMake: promote the system BZip2 imported target to global scope so
WITH_BZIP2=SYSTEMresolves from the top-level directory. - Tests: silence
-Werror=unused-variableunder GCC in the AES/Camellia/Serpent/Twofish symmetric crypter tests, where the decrypt result is intentionally unchecked (CBC either fails padding or succeeds with garbage).
Notes
- Decompression of untrusted input is not size-bounded by this module (same as the underlying zlib/libbzip2); callers must bound it themselves. See README.md.
- Needs a C++26 compiler, StormByte Base ≥ 1.1.0, StormByte Buffer ≥ 1.1.0, and Crypto++ at build time.