SHA-256
Header: #include <cryptopp/sha.h> | Namespace: CryptoPP
Since: Crypto++ 4.0
Thread Safety: Not thread-safe per instance; use separate instances per thread
SHA-256 (Secure Hash Algorithm 256-bit) is a widely-used cryptographic hash function from the SHA-2 family. It’s a NIST/FIPS standard used in TLS, Bitcoin, digital signatures, and countless security applications.
Quick Example
#include <cryptopp/sha.h>
#include <cryptopp/hex.h>
#include <cryptopp/filters.h>
#include <iostream>
int main() {
using namespace CryptoPP;
SHA256 hash;
std::string message = "Hello, World!";
std::string digest, hexOutput;
StringSource(message, true,
new HashFilter(hash,
new StringSink(digest)
)
);
// Convert to hex
StringSource(digest, true,
new HexEncoder(new StringSink(hexOutput))
);
std::cout << "SHA-256: " << hexOutput << std::endl;
// Expected: dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
return 0;
}Usage Guidelines
Do:
- Use SHA-256 for general-purpose hashing (file integrity, checksums)
- Use for digital signatures and certificates
- Use with HMAC for message authentication
- Use as the underlying hash in PBKDF2 (PBKDF2-HMAC-SHA256)
- Use as a fast follow-on KDF after Argon2 (once you already have a high-entropy key)
- Generate test vectors for validation
Avoid:
- Hashing passwords directly with plain SHA-256 (use Argon2 for password hashing)
- Using SHA-1 for new applications (broken, use SHA-256)
- Assuming collision resistance means preimage resistance
Class: SHA256
Cryptographic hash function producing 256-bit (32-byte) digests.
Constants
static const int DIGESTSIZE = 32; // 32 bytes (256 bits)
static const int BLOCKSIZE = 64; // 64 bytes (512 bits)
Constructors
Default Constructor
SHA256();Create SHA-256 hash object in initial state.
Example:
SHA256 hash;Methods
Update()
void Update(const byte* input, size_t length);Add data to the hash computation.
Parameters:
input- Data to hashlength- Length of data in bytes
Can be called multiple times to hash data incrementally.
Example:
SHA256 hash;
hash.Update((const byte*)"Hello, ", 7);
hash.Update((const byte*)"World!", 6);
// Equivalent to hashing "Hello, World!"
Final()
void Final(byte* digest);Finalize hash and get result.
Parameters:
digest- Output buffer (must be 32 bytes)
Note: Automatically calls Restart() after completion.
Example:
SHA256 hash;
std::string message = "Hello, World!";
hash.Update((const byte*)message.data(), message.size());
byte digest[SHA256::DIGESTSIZE];
hash.Final(digest);TruncatedFinal()
void TruncatedFinal(byte* digest, size_t digestSize);Get truncated hash (first digestSize bytes).
Parameters:
digest- Output bufferdigestSize- Number of bytes to output (≤ 32)
Use case: Some protocols require shorter hashes (e.g., 128-bit).
Example:
SHA256 hash;
hash.Update((const byte*)data, dataLen);
byte shortHash[16]; // 128-bit hash
hash.TruncatedFinal(shortHash, 16);Restart()
void Restart();Reset hash to initial state. Keeps object for reuse.
Example:
SHA256 hash;
// Hash first message
hash.Update((const byte*)msg1.data(), msg1.size());
byte digest1[32];
hash.Final(digest1);
// Reuse for second message
hash.Update((const byte*)msg2.data(), msg2.size());
byte digest2[32];
hash.Final(digest2);CalculateDigest() - Static
static void CalculateDigest(byte* digest,
const byte* input,
size_t length);One-shot hash computation.
Parameters:
digest- Output buffer (32 bytes)input- Data to hashlength- Length of data
Example:
std::string message = "Hello, World!";
byte digest[SHA256::DIGESTSIZE];
SHA256::CalculateDigest(digest,
(const byte*)message.data(),
message.size()
);VerifyDigest() - Static
static bool VerifyDigest(const byte* digest,
const byte* input,
size_t length);Verify a hash digest (constant-time comparison).
Parameters:
digest- Expected hash (32 bytes)input- Data to verifylength- Length of data
Returns: true if hash matches, false otherwise
Important: Uses constant-time comparison to prevent timing attacks.
Example:
byte expectedHash[32] = { /* ... */ };
std::string data = "Test data";
bool valid = SHA256::VerifyDigest(
expectedHash,
(const byte*)data.data(),
data.size()
);
if (valid) {
std::cout << "Hash verified!" << std::endl;
}DigestSize()
unsigned int DigestSize() const;Get hash output size.
Returns: 32 (bytes)
AlgorithmName()
std::string AlgorithmName() const;Get algorithm name.
Returns: “SHA-256”
AlgorithmProvider()
std::string AlgorithmProvider() const;Get implementation provider (hardware acceleration info).
Returns:
- “SHA-NI” - Intel/AMD SHA Extensions
- “ARMv8” - ARM SHA Extensions
- “POWER8” - IBM POWER8+ SHA
- “C++” - Software implementation
Example:
SHA256 hash;
std::cout << "Provider: " << hash.AlgorithmProvider() << std::endl;
// Output: "SHA-NI" on modern Intel/AMD CPUs
Complete Example: File Hashing
#include <cryptopp/sha.h>
#include <cryptopp/files.h>
#include <cryptopp/hex.h>
#include <iostream>
using namespace CryptoPP;
std::string hashFile(const std::string& filename) {
SHA256 hash;
std::string digest, hexDigest;
FileSource(filename.c_str(), true,
new HashFilter(hash,
new StringSink(digest)
)
);
StringSource(digest, true,
new HexEncoder(new StringSink(hexDigest))
);
return hexDigest;
}
int main() {
try {
std::string hash = hashFile("document.pdf");
std::cout << "SHA-256: " << hash << std::endl;
// Save hash for later verification
std::ofstream out("document.pdf.sha256");
out << hash;
} catch (const Exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}Complete Example: Incremental Hashing
#include <cryptopp/sha.h>
#include <cryptopp/hex.h>
#include <fstream>
#include <iostream>
using namespace CryptoPP;
std::string hashLargeFile(const std::string& filename) {
SHA256 hash;
std::ifstream file(filename, std::ios::binary);
if (!file) {
throw std::runtime_error("Cannot open file");
}
// Process in 64KB chunks
const size_t BUFFER_SIZE = 65536;
byte buffer[BUFFER_SIZE];
while (file.read((char*)buffer, BUFFER_SIZE) || file.gcount() > 0) {
hash.Update(buffer, file.gcount());
}
byte digest[SHA256::DIGESTSIZE];
hash.Final(digest);
// Convert to hex
std::string hexDigest;
StringSource(digest, sizeof(digest), true,
new HexEncoder(new StringSink(hexDigest))
);
return hexDigest;
}
int main() {
std::string hash = hashLargeFile("large_video.mp4");
std::cout << "SHA-256: " << hash << std::endl;
return 0;
}Complete Example: HMAC-SHA256
#include <cryptopp/sha.h>
#include <cryptopp/hmac.h>
#include <cryptopp/filters.h>
#include <cryptopp/hex.h>
#include <iostream>
using namespace CryptoPP;
std::string hmacSHA256(const std::string& key, const std::string& message) {
HMAC<SHA256> hmac((const byte*)key.data(), key.size());
std::string mac, hexMAC;
StringSource(message, true,
new HashFilter(hmac,
new StringSink(mac)
)
);
StringSource(mac, true,
new HexEncoder(new StringSink(hexMAC))
);
return hexMAC;
}
int main() {
std::string key = "secret-key-2025";
std::string message = "Important message";
std::string mac = hmacSHA256(key, message);
std::cout << "HMAC-SHA256: " << mac << std::endl;
return 0;
}Performance
Hardware Acceleration
SHA-256 benefits from hardware acceleration on modern processors:
| Platform | Instructions | Speedup |
|---|---|---|
| Intel/AMD x86-64 | SHA Extensions (SHA-NI) | 3-5x |
| ARM v8+ | SHA Extensions | 3-5x |
| IBM POWER8+ | SHA instructions | 3-5x |
| Software | Pure C++ | Baseline |
Benchmarks (Approximate)
| Operation | Speed (MB/s) | Notes |
|---|---|---|
| With SHA-NI | 800-1500 | Hardware accelerated |
| Without | 200-400 | Software implementation |
| Incremental | Same | No overhead |
Note: BLAKE3 is 2-4x faster than SHA-256, even with SHA-NI.
Checking Hardware Support
SHA256 hash;
std::string provider = hash.AlgorithmProvider();
if (provider == "SHA-NI" || provider == "ARMv8") {
std::cout << "Hardware acceleration available!" << std::endl;
} else {
std::cout << "Using software implementation" << std::endl;
}Security
Security Properties
- Collision resistance: 128-bit security (2^128 operations)
- Preimage resistance: 256-bit security (2^256 operations)
- Second preimage: 256-bit security
- Output size: 256 bits (32 bytes)
- Block size: 512 bits (64 bytes)
- Standard: FIPS 180-4, NIST approved
Security Level
SHA-256 provides 128-bit collision resistance (birthday bound) and 256-bit preimage resistance.
Comparison:
- SHA-256: ~128-bit collision / ~256-bit preimage
- SHA-512: ~256-bit collision / ~512-bit preimage
- BLAKE3: ~128-bit collision / ~128-bit preimage (but faster in software)
Important Security Notes
Not for Password Hashing:
// WRONG - SHA-256 is too fast for passwords std::string passwordHash = sha256(password); // CORRECT - Use Argon2 Argon2 argon2; argon2.DeriveKey(hash, ...);Collision Resistance vs Preimage:
- Collision: Finding two messages with same hash (~128-bit security)
- Preimage: Finding message for given hash (~256-bit security)
- Most applications are comfortable with ~128-bit overall security; SHA-256 exceeds that for both collision and preimage resistance
SHA-1 is Broken:
// NEVER use SHA-1 for new applications SHA1 broken; // Collision attacks exist! // Use SHA-256 instead SHA256 secure;
Test Vectors (NIST)
// Test Vector 1: Empty string
// Input: ""
// SHA-256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
SHA256 hash;
byte digest[32];
hash.Final(digest);
byte expected[] = {
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55
};
assert(std::memcmp(digest, expected, 32) == 0);// Test Vector 2: "abc"
// Input: "abc"
// SHA-256: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
std::string message = "abc";
SHA256 hash;
hash.Update((const byte*)message.data(), message.size());
byte digest[32];
hash.Final(digest);
byte expected[] = {
0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad
};
assert(std::memcmp(digest, expected, 32) == 0);Common Use Cases
1. File Integrity Verification
// Generate checksum
std::string originalHash = hashFile("software.zip");
// Later: verify file hasn't been modified
std::string currentHash = hashFile("software.zip");
if (originalHash == currentHash) {
std::cout << "File integrity verified" << std::endl;
} else {
std::cout << "WARNING: File has been modified!" << std::endl;
}2. Content-Addressed Storage
// Store files by their SHA-256 hash
std::string content = readFile("photo.jpg");
std::string hash = sha256(content);
// Save as: storage/ba/7816bf.../content
std::string path = "storage/" + hash.substr(0, 2) + "/" +
hash.substr(2, 6) + "/content";
writeFile(path, content);3. Digital Signatures (with RSA/ECDSA)
// Sign document
std::string document = readFile("contract.pdf");
SHA256 hash;
hash.Update((const byte*)document.data(), document.size());
byte digest[32];
hash.Final(digest);
// Sign the hash (not the whole document)
RSA::Signer signer(privateKey);
std::string signature = sign(digest, 32, signer);4. Merkle Trees (Blockchain)
// Build Merkle tree from transaction hashes
std::vector<std::string> transactions;
std::vector<byte[32]> leafHashes;
for (const auto& tx : transactions) {
byte hash[32];
SHA256::CalculateDigest(hash,
(const byte*)tx.data(), tx.size());
leafHashes.push_back(hash);
}
// Combine hashes pairwise
// ...
Thread Safety
Not thread-safe. Use separate instances per thread.
// WRONG - sharing between threads
SHA256 global_hash;
void thread1() { global_hash.Update(...); } // RACE CONDITION
void thread2() { global_hash.Update(...); }
// CORRECT - per-thread instances
void threadFunc(const std::string& data) {
SHA256 hash; // Thread-local
hash.Update((const byte*)data.data(), data.size());
byte digest[32];
hash.Final(digest);
}When to Use SHA-256
✅ Use SHA-256 for:
- File Integrity - Checksums, downloads verification
- Digital Signatures - Hash before signing
- Certificates - TLS/SSL certificates
- Blockchain - Bitcoin, Ethereum (though moving to SHA-3)
- HMAC - Message authentication (HMAC-SHA256)
- General Hashing - When you need a standard hash
❌ Don’t use SHA-256 for:
- Password Hashing - Use Argon2 (SHA-256 is too fast)
- High Performance - Use BLAKE3 (2-4x faster)
- New Protocols - Consider BLAKE3 or SHA-3 for future-proofing
SHA-256 vs Alternatives
| Algorithm | Speed | Security | Standard | Use Case |
|---|---|---|---|---|
| SHA-256 | Fast | 128-bit collision | NIST/FIPS | General purpose ⭐ |
| BLAKE3 | Very Fast | 128-bit collision | Modern | High performance |
| SHA-512 | Fast (64-bit) | 256-bit collision | NIST/FIPS | High security |
| SHA-3 | Medium | 128-bit collision | NIST/FIPS | Diversity |
| SHA-1 | Fast | BROKEN | Deprecated | NEVER use |
Exceptions
None thrown under normal operation. Memory allocation failures may throw std::bad_alloc.
See Also
- SHA-512 - 512-bit variant
- SHA-3 - Alternative SHA family
- BLAKE3 - Faster modern hash
- HMAC - Message authentication with SHA-256
- Hash Functions Guide - Conceptual overview
- Security Concepts - Understanding cryptographic hashes