Domain Track
Difficulty 3/5Security and Cryptography in C++
C++ cryptography with OpenSSL (EVP API), libsodium, secure memory handling, constant-time comparisons, TLS with Asio SSL, and common security pitfalls.
TL;DR
Never roll your own crypto. Use OpenSSL's EVP API for standard algorithms or libsodium for a safer, higher-level interface. Always check return codes, zero sensitive buffers, and use constant-time comparison for secrets.
cpp
// libsodium symmetric encryption
unsigned char key[crypto_secretbox_KEYBYTES];
unsigned char nonce[crypto_secretbox_NONCEBYTES];
randombytes_buf(key, sizeof key);
randombytes_buf(nonce, sizeof nonce);
std::vector<unsigned char> ciphertext(msg.size() + crypto_secretbox_MACBYTES);
crypto_secretbox_easy(ciphertext.data(), msg.data(), msg.size(), nonce, key);OpenSSL EVP: AES-256-GCM Encryption
cpp
#include <openssl/evp.h>
#include <vector>
#include <stdexcept>
// Returns {ciphertext, tag}
std::pair<std::vector<unsigned char>, std::array<unsigned char,16>>
aes_gcm_encrypt(const unsigned char* key, // 32 bytes
const unsigned char* iv, // 12 bytes recommended
const unsigned char* plaintext, size_t pt_len,
const unsigned char* aad, // additional authenticated data
size_t aad_len) {
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if (!ctx) throw std::runtime_error("EVP_CIPHER_CTX_new failed");
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, iv);
int len = 0;
if (aad && aad_len > 0)
EVP_EncryptUpdate(ctx, nullptr, &len, aad, (int)aad_len);
std::vector<unsigned char> ciphertext(pt_len);
EVP_EncryptUpdate(ctx, ciphertext.data(), &len, plaintext, (int)pt_len);
int ct_len = len;
EVP_EncryptFinal_ex(ctx, ciphertext.data() + ct_len, &len);
std::array<unsigned char, 16> tag;
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag.data());
EVP_CIPHER_CTX_free(ctx);
return {ciphertext, tag};
}OpenSSL EVP: SHA-256 Hash
cpp
#include <openssl/evp.h>
#include <array>
std::array<unsigned char, 32> sha256(const unsigned char* data, size_t len) {
EVP_MD_CTX* ctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr);
EVP_DigestUpdate(ctx, data, len);
std::array<unsigned char, 32> digest;
unsigned int digest_len = 32;
EVP_DigestFinal_ex(ctx, digest.data(), &digest_len);
EVP_MD_CTX_free(ctx);
return digest;
}
// Hex representation
std::string to_hex(std::span<const unsigned char> bytes) {
std::string result;
result.reserve(bytes.size() * 2);
for (auto b : bytes)
result += std::format("{:02x}", b);
return result;
}OpenSSL EVP: HMAC-SHA256
cpp
#include <openssl/hmac.h>
std::array<unsigned char, 32> hmac_sha256(
const unsigned char* key, size_t key_len,
const unsigned char* data, size_t data_len) {
std::array<unsigned char, 32> result;
unsigned int result_len = 32;
HMAC(EVP_sha256(), key, (int)key_len,
data, data_len,
result.data(), &result_len);
return result;
}libsodium: Symmetric Encryption
cpp
#include <sodium.h>
// Initialize once at startup
if (sodium_init() < 0) throw std::runtime_error("libsodium init failed");
// Encrypt
void encrypt_message(const std::string& message) {
unsigned char key[crypto_secretbox_KEYBYTES]; // 32 bytes
unsigned char nonce[crypto_secretbox_NONCEBYTES]; // 24 bytes
crypto_secretbox_keygen(key);
randombytes_buf(nonce, sizeof nonce);
std::vector<unsigned char> ciphertext(
message.size() + crypto_secretbox_MACBYTES);
crypto_secretbox_easy(
ciphertext.data(),
reinterpret_cast<const unsigned char*>(message.data()),
message.size(), nonce, key);
}
// Decrypt
bool decrypt_message(
const unsigned char* ciphertext, size_t ct_len,
const unsigned char* nonce, const unsigned char* key,
std::string& out) {
std::vector<unsigned char> plaintext(ct_len - crypto_secretbox_MACBYTES);
if (crypto_secretbox_open_easy(
plaintext.data(), ciphertext, ct_len, nonce, key) != 0)
return false; // authentication failed — tampered or wrong key
out.assign(plaintext.begin(), plaintext.end());
return true;
}libsodium: Ed25519 Signatures
cpp
// Generate a keypair
unsigned char pk[crypto_sign_PUBLICKEYBYTES];
unsigned char sk[crypto_sign_SECRETKEYBYTES];
crypto_sign_keypair(pk, sk);
// Sign a message
std::string message = "authenticate this";
std::vector<unsigned char> sig(crypto_sign_BYTES);
unsigned long long sig_len;
crypto_sign_detached(
sig.data(), &sig_len,
reinterpret_cast<const unsigned char*>(message.data()),
message.size(), sk);
// Verify
bool ok = crypto_sign_verify_detached(
sig.data(),
reinterpret_cast<const unsigned char*>(message.data()),
message.size(), pk) == 0;libsodium: Password Hashing (Argon2id)
cpp
char hash[crypto_pwhash_STRBYTES];
const char* password = "hunter2";
// Hash (for storage)
if (crypto_pwhash_str(
hash, password, strlen(password),
crypto_pwhash_OPSLIMIT_INTERACTIVE,
crypto_pwhash_MEMLIMIT_INTERACTIVE) != 0)
throw std::runtime_error("out of memory for hashing");
// Verify
bool matches = crypto_pwhash_str_verify(
hash, password, strlen(password)) == 0;Secure Memory Handling
cpp
// Allocate memory that won't be swapped or appear in core dumps
void* secret = sodium_malloc(32); // guarded allocation
// or:
unsigned char key[32];
sodium_mlock(key, sizeof key); // prevent swapping
// ALWAYS zero sensitive data before freeing
sodium_memzero(key, sizeof key); // libsodium — compiler can't optimize away
OPENSSL_cleanse(key, sizeof key); // OpenSSL equivalent
// Don't use memset() — optimizer may remove it if buffer is "dead"
// volatile trick (if no library available)
volatile unsigned char* p = key;
for (size_t i = 0; i < sizeof key; ++i) p[i] = 0;Constant-Time Comparison
cpp
// WRONG: early exit leaks timing info about secret
bool bad_compare(const char* a, const char* b, size_t len) {
for (size_t i = 0; i < len; ++i)
if (a[i] != b[i]) return false; // attacker can measure response time
return true;
}
// CORRECT: constant-time comparison
bool ct_compare(const unsigned char* a, const unsigned char* b, size_t len) {
return CRYPTO_memcmp(a, b, len) == 0; // OpenSSL
// or: sodium_memcmp(a, b, len) == 0; // libsodium
}TLS with Asio SSL
cpp
#include <asio/ssl.hpp>
asio::io_context io;
asio::ssl::context ssl_ctx(asio::ssl::context::tls_client);
// Configure
ssl_ctx.set_verify_mode(asio::ssl::verify_peer);
ssl_ctx.set_default_verify_paths();
ssl_ctx.load_verify_file("ca.crt");
// Create SSL stream
using SSLStream = asio::ssl::stream<asio::ip::tcp::socket>;
SSLStream stream(io, ssl_ctx);
// Set SNI hostname (required for many servers)
SSL_set_tlsext_host_name(stream.native_handle(), "example.com");
// Connect and handshake
asio::ip::tcp::resolver resolver(io);
auto endpoints = resolver.resolve("example.com", "443");
asio::connect(stream.next_layer(), endpoints);
stream.handshake(asio::ssl::stream_base::client);
// Read/write like a normal socket
asio::write(stream, asio::buffer("GET / HTTP/1.0\r\n\r\n"));
std::string response;
asio::error_code ec;
asio::read(stream, asio::dynamic_buffer(response), ec);Common Pitfalls
cpp
1. Nonce/IV reuse — catastrophic for GCM/CTR modes (breaks confidentiality)
Fix: use a counter or random nonce, never reuse
2. Not checking return values — OpenSSL returns 1 for success
Fix: always check EVP_* return codes
3. Weak RNG — using rand() or time-based seeds for crypto
Fix: use randombytes_buf() (libsodium) or RAND_bytes() (OpenSSL)
4. Key material in core dumps / swap
Fix: mlock(), guarded allocations, zero on free
5. Timing attacks on secret comparison
Fix: use constant-time compare (CRYPTO_memcmp, sodium_memcmp)
6. Using ECB mode — reveals plaintext patterns
Fix: use GCM (authenticated) or CBC with HMAC
7. Short keys — AES-128 is fine, never go below 128 bitsLibrary Comparison
| OpenSSL | libsodium | Botan | mbedTLS | |
|---|---|---|---|---|
| API complexity | High (EVP is verbose) | Low (opinionated) | Medium | Medium |
| Algorithm choice | Unlimited | Curated safe set | Wide | Wide |
| TLS/DTLS | Yes | No (use libsodium + hand-roll) | Yes | Yes (embedded focus) |
| Footprint | Large | Small | Medium | Small |
| License | Apache 2.0 | ISC | Simplified BSD | Apache 2.0 |
| Best for | General use, TLS | Modern crypto, easy API | Wide algorithm support | Embedded/IoT |