Domain Track
Difficulty 3/5Cryptography in C++
C++ cryptography — OpenSSL hashing, encryption/decryption, random bytes, HMAC, TLS with Asio, and libsodium for modern crypto.
TL;DR
Use libsodium for new code — simple, hard to misuse. Use OpenSSL when you need protocol-level TLS control. Never implement your own crypto. Always use secure random for keys and nonces.
Hashing with OpenSSL (EVP API)
cpp
#include <openssl/evp.h>
#include <vector>
#include <string>
std::vector<uint8_t> sha256(std::string_view data) {
unsigned int len = 0;
std::vector<uint8_t> hash(EVP_MAX_MD_SIZE);
EVP_MD_CTX* ctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr);
EVP_DigestUpdate(ctx, data.data(), data.size());
EVP_DigestFinal_ex(ctx, hash.data(), &len);
EVP_MD_CTX_free(ctx);
hash.resize(len); // 32 bytes for SHA-256
return hash;
}
// Hex encoding
std::string to_hex(std::span<const uint8_t> bytes) {
std::string hex;
hex.reserve(bytes.size() * 2);
for (uint8_t b : bytes)
std::format_to(std::back_inserter(hex), "{:02x}", b);
return hex;
}
// Usage
auto hash = sha256("hello world");
std::println("SHA-256: {}", to_hex(hash));
// SHA-256: b94d27b9934d3e08a52e52d7da7dabfac484efe04294e576b7...HMAC-SHA256
For message authentication (verify data was not tampered with):
cpp
#include <openssl/hmac.h>
std::vector<uint8_t> hmac_sha256(std::string_view key, std::string_view data) {
unsigned int len = 0;
std::vector<uint8_t> result(EVP_MAX_MD_SIZE);
HMAC(EVP_sha256(),
key.data(), static_cast<int>(key.size()),
reinterpret_cast<const uint8_t*>(data.data()), data.size(),
result.data(), &len);
result.resize(len);
return result;
}
// Constant-time comparison (prevent timing attacks)
bool secure_equals(std::span<const uint8_t> a, std::span<const uint8_t> b) {
if (a.size() != b.size()) return false;
return CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0;
}
// Verify
auto expected_mac = hmac_sha256("secret-key", "data");
auto received_mac = /* from request */;
if (secure_equals(expected_mac, received_mac))
std::println("valid");Symmetric Encryption: AES-256-GCM
Authenticated encryption — provides confidentiality + integrity:
cpp
#include <openssl/evp.h>
#include <stdexcept>
struct AesGcmCiphertext {
std::vector<uint8_t> nonce; // 12 bytes
std::vector<uint8_t> tag; // 16 bytes
std::vector<uint8_t> ciphertext;
};
AesGcmCiphertext aes256gcm_encrypt(
std::span<const uint8_t> key, // must be 32 bytes
std::span<const uint8_t> plaintext)
{
AesGcmCiphertext result;
result.nonce.resize(12);
RAND_bytes(result.nonce.data(), 12); // random nonce — never reuse!
result.ciphertext.resize(plaintext.size());
result.tag.resize(16);
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr);
EVP_EncryptInit_ex(ctx, nullptr, nullptr, key.data(), result.nonce.data());
int out_len;
EVP_EncryptUpdate(ctx, result.ciphertext.data(), &out_len,
plaintext.data(), plaintext.size());
EVP_EncryptFinal_ex(ctx, result.ciphertext.data() + out_len, &out_len);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, result.tag.data());
EVP_CIPHER_CTX_free(ctx);
return result;
}Secure Random Numbers
cpp
#include <openssl/rand.h>
// Cryptographically secure random bytes
std::vector<uint8_t> random_bytes(size_t n) {
std::vector<uint8_t> buf(n);
if (RAND_bytes(buf.data(), static_cast<int>(n)) != 1)
throw std::runtime_error("RAND_bytes failed");
return buf;
}
// Generate a 256-bit key
auto key = random_bytes(32);
// Generate a UUID v4 (using OpenSSL random)
std::string uuid_v4() {
auto bytes = random_bytes(16);
bytes[6] = (bytes[6] & 0x0F) | 0x40; // version 4
bytes[8] = (bytes[8] & 0x3F) | 0x80; // variant RFC 4122
return std::format(
"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-"
"{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
bytes[0], bytes[1], bytes[2], bytes[3],
bytes[4], bytes[5], bytes[6], bytes[7],
bytes[8], bytes[9], bytes[10], bytes[11],
bytes[12], bytes[13], bytes[14], bytes[15]);
}libsodium — Modern Simple API
libsodium is easier to use correctly than OpenSSL:
cpp
#include <sodium.h>
// Initialize (call once at startup)
if (sodium_init() < 0) throw std::runtime_error("libsodium init failed");
// Symmetric encryption: XSalsa20-Poly1305 (authenticated)
void secret_box_example() {
// Generate key
uint8_t key[crypto_secretbox_KEYBYTES];
crypto_secretbox_keygen(key);
// Generate nonce (random, unique per message)
uint8_t nonce[crypto_secretbox_NONCEBYTES];
randombytes_buf(nonce, sizeof(nonce));
std::string message = "secret message";
std::vector<uint8_t> ciphertext(crypto_secretbox_MACBYTES + message.size());
crypto_secretbox_easy(ciphertext.data(),
reinterpret_cast<const uint8_t*>(message.data()), message.size(),
nonce, key);
// Decrypt + verify
std::vector<uint8_t> plaintext(message.size());
if (crypto_secretbox_open_easy(plaintext.data(), ciphertext.data(),
ciphertext.size(), nonce, key) != 0)
throw std::runtime_error("decryption failed or tampered");
}
// Password hashing (Argon2id)
void password_hash_example() {
char hash[crypto_pwhash_STRBYTES];
std::string password = "user-password";
if (crypto_pwhash_str(hash, password.data(), password.size(),
crypto_pwhash_OPSLIMIT_INTERACTIVE,
crypto_pwhash_MEMLIMIT_INTERACTIVE) != 0)
throw std::runtime_error("OOM");
// Verify
bool valid = crypto_pwhash_str_verify(
hash, password.data(), password.size()) == 0;
}CMake Setup
cmake
find_package(OpenSSL REQUIRED)
target_link_libraries(myapp PRIVATE OpenSSL::SSL OpenSSL::Crypto)
# libsodium (via pkg-config or manual)
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBSODIUM REQUIRED libsodium)
target_link_libraries(myapp PRIVATE ${LIBSODIUM_LIBRARIES})
target_include_directories(myapp PRIVATE ${LIBSODIUM_INCLUDE_DIRS})Checklist
- Never implement your own crypto primitives
- Use AES-256-GCM or ChaCha20-Poly1305 for symmetric encryption
- Use ECDH (X25519) or RSA-OAEP for key exchange
- Never reuse nonces/IVs with the same key
- Use constant-time comparison for MAC verification (
CRYPTO_memcmp) - Hash passwords with Argon2id/bcrypt/scrypt — never SHA-256 alone
- Generate keys/nonces with
RAND_bytesorrandombytes_buf