A lightweight, portable C library implementing a wide range of SHA algorithms, fully enabled by default and optimized for both little-endian and big-endian systems.
- SHA-1/2 variants: SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256
- SHA-3 variants: SHA3-224, SHA3-256, SHA3-384, SHA3-512
- SHAKE / XOF variants: SHAKE128, SHAKE256
- Raw SHAKE / Raw Keccak: RawSHAKE128, RawSHAKE256 (for raw Keccak; can be enabled via
ENABLE_RAW_KECCAK) - Separate implementation file (
tiny_sha.c) and header (tiny_sha.h) - Incremental (streaming) API:
Init,Absorb/Update,Final,Squeeze(all returnbool) - Wrapper functions for one-shot hashing
- Safe Constant-time hash equality comparison using
ConstTimeCompare, returning 1 if equal or 0 if different, without leaking timing. - Handles endianness automatically
- Lightweight — entire library under 30 KB
The library allows enabling or disabling specific hash algorithms. By default, all are enabled.
You can define flags via CMake for proper build integration:
# Tiny-SHA feature flags
target_compile_definitions(test_sha PRIVATE
ENABLE_SHA1=1
ENABLE_SHA224=1
ENABLE_SHA256=1
ENABLE_SHA384=1
ENABLE_SHA512=1
ENABLE_SHA512_224=1
ENABLE_SHA512_256=1
ENABLE_SHA3_224=1
ENABLE_SHA3_256=1
ENABLE_SHA3_384=1
ENABLE_SHA3_512=1
ENABLE_SHAKE128=1
ENABLE_SHAKE256=1
ENABLE_RAWSHAKE128=1
ENABLE_RAWSHAKE256=1
ENABLE_RAW_KECCAK=0 # off by default
TSHASH_PREFIX=MyLib_ # optional function prefix
)The header handles internal dependencies automatically:
- SHA-224 → uses SHA-256 internally.
- SHA-384 → uses SHA-512 internally.
- SHA-512/224 and SHA-512/256 → use SHA-512 internally.
- SHA-3 (224, 256, 384, 512) and SHAKE (128, 256) → use internal Keccak permutation functions (
k_init_wrap,k_absorb_wrap,k_final_wrap,k_squeeze_wrap) for processing.
To avoid name collisions, you can add a prefix to all functions:
#define TSHASH_PREFIX MyLib_ // prefix for all functions
#include "tiny_sha.h"All functions will now have the prefix:
MyLib_SHA256(...); // wrapper function
MyLib_sha256_init(...); // incremental APINote:
TSHASH_PREFIXmust be defined before including the header. Otherwise, functions will have no prefix (default behavior).
- Open a terminal and navigate to your project folder:
$ cd /path/to/repo/tiny-sha- Create and enter the
builddirectory:
$ mkdir build
$ cd build- Configure the project:
$ cmake ..- Build the project (Release mode recommended):
$ cmake --build . --config Release- The final binary will be located in:
$ build/bin/Release/test_sha.exe
#include <stdio.h>
#include "tiny_sha.h"
int main() {
const char *msg = "Hello, Tiny SHA!";
uint8_t hash[SHA256_DIGEST_SIZE];
if (SHA256((const uint8_t*)msg, strlen(msg), hash)) {
printf("SHA-256: ");
for (int i = 0; i < SHA256_DIGEST_SIZE; i++) {
printf("%02x", hash[i]);
}
printf("\n");
} else {
printf("SHA-256 computation failed!\n");
}
return 0;
}#include <stdio.h>
#include "tiny_sha.h"
int main() {
const char *msg = "Hello, Tiny SHA!";
uint8_t hash[SHA256_DIGEST_SIZE];
SHA256_CTX ctx;
if (sha256_init(&ctx) &&
sha256_update(&ctx, (const uint8_t*)msg, strlen(msg)) &&
sha256_final(&ctx, hash)) {
printf("SHA-256: ");
for (int i = 0; i < SHA256_DIGEST_SIZE; i++) {
printf("%02x", hash[i]);
}
printf("\n");
} else {
printf("SHA-256 computation failed!\n");
}
return 0;
}uint8_t hash1[SHA256_DIGEST_SIZE];
uint8_t hash2[SHA256_DIGEST_SIZE];
// Compute hash1 and hash2...
int cmp = SHA256CompareOrder(hash1, hash2);
if (cmp == 0) {
printf("Hashes are equal\n");
} else if (cmp < 0) {
printf("hash1 < hash2\n");
} else {
printf("hash1 > hash2\n");
}| Algorithm | Digest Size |
|---|---|
| SHA-1 | 20 bytes |
| SHA-224 | 28 bytes |
| SHA-256 | 32 bytes |
| SHA-384 | 48 bytes |
| SHA-512 | 64 bytes |
| SHA-512/224 | 28 bytes |
| SHA-512/256 | 32 bytes |
| SHA3-224 | 28 bytes |
| SHA3-256 | 32 bytes |
| SHA3-384 | 48 bytes |
| SHA3-512 | 64 bytes |
| SHAKE128 | variable |
| SHAKE256 | variable |
| RawSHAKE128 | variable |
| RawSHAKE256 | variable |
- Fully self-contained — no external dependencies.
- All functions return
boolto indicate success or failure. - Designed for simplicity, speed, and easy integration.
- SHA-3 functions correspond to SHA-2 operations:
Init(SHA-2) →Init(SHA-3)Update(SHA-2) →Absorb(SHA-3)Final(SHA-2) →Final/Squeeze(SHA-3)
- One-shot wrapper functions follow the same style across all algorithms, ensuring a consistent API.
- SHAKE and RawSHAKE functions support variable-length output for flexible byte-level operations.
- Raw Keccak API can be enabled via
ENABLE_RAW_KECCAK. - Optional bit-level helpers (enabled with
ENABLE_SHAKE128orENABLE_SHAKE256):Trunc_s(X, Xlen, s, out)— truncates a byte arrayXto the firstsbits, storing the result inout.concat_bits(X, x_bits, Y, y_bits, out)— concatenatesx_bitsfromXandy_bitsfromYintoout.
Hi!
I wrote Tiny SHA because I wanted a small, self-contained hashing library I can easily include in my C projects (for example, my PE dumper) without pulling in large dependencies like OpenSSL or relying on copying/stealing other people’s code. It's both a learning tool, helping me understand padding, endianness, and incremental hashing, and a practical library: configurable (enable/disable algorithms), prefixable to avoid name collisions, and straightforward to compile and link. The code is minimal, auditable, and easy to extend. :)
- RFC 6234 – US Secure Hash Algorithms (SHA and SHA‑based HMAC and HKDF), May 2011
- FIPS PUB 202 – SHA‑3 Standard: Permutation-Based Hash and Extendable-Output Functions, August 2015
This project is released under the MIT License. See LICENSE for full text.