Hashes

The Fossil Cryptic Hash library is a versatile, cross-platform hashing toolkit designed for both C and C++ development. It supports multiple algorithms, including CRC32, FNV-1a (32 and 64-bit), Murmur3, and cryptographic SHA-256, providing both one-shot and streaming interfaces for flexible use cases. The C API allows low-level, efficient hash computations, while the C++ wrapper offers an object-oriented, STL-friendly interface with exception safety and convenient hex-string conversions. This design enables developers to compute hashes incrementally or in a single call, making it suitable for data integrity checks, digital signatures, and fast non-cryptographic hashing, all within a unified, easy-to-use framework.

HEADER REFERENCE #

#ifndef FOSSIL_CRYPTIC_HASH_H
#define FOSSIL_CRYPTIC_HASH_H

#include <stdint.h>
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

/* ---------------------
 * Types and constants
 * -------------------*/

/* Add in the enum */
typedef enum {
    FOSSIL_CRYPTIC_HASH_ALG_CRC32 = 1,
    FOSSIL_CRYPTIC_HASH_ALG_FNV1A32,
    FOSSIL_CRYPTIC_HASH_ALG_FNV1A64,
    FOSSIL_CRYPTIC_HASH_ALG_MURMUR3_32,
    FOSSIL_CRYPTIC_HASH_ALG_SHA256     /* NEW */
} fossil_cryptic_hash_alg_t;

/* SHA-256 context (internal, but exposed for size) */
typedef struct {
    uint32_t state[8];
    uint64_t bitlen;
    uint8_t  buffer[64];
} fossil_cryptic_hash_sha256_ctx_t;

/* Add to generic context union */
typedef struct {
    fossil_cryptic_hash_alg_t alg;
    union {
        uint32_t crc32;
        uint32_t fnv1a32;
        uint64_t fnv1a64;
        fossil_cryptic_hash_sha256_ctx_t sha256;
    } state;
} fossil_cryptic_hash_ctx_t;

// functions

/* --- One-shot SHA-256 --- */

/**
 * @brief Compute a SHA-256 digest for a complete data buffer in one call.
 *
 * This is a one-shot API — it processes the entire data block and produces
 * the 256-bit digest without maintaining a streaming context.
 *
 * @param data Pointer to the input data buffer.
 * @param len  Length of the input data in bytes.
 * @param out  Pointer to a 32-byte array where the resulting hash will be stored.
 */
void fossil_cryptic_hash_sha256(const void *data, size_t len, uint8_t out[32]);

/* --- Streaming SHA-256 --- */

/**
 * @brief Initialize a SHA-256 hashing context for incremental hashing.
 *
 * This function prepares the provided SHA-256 context for accepting data.
 * It must be called before any calls to fossil_cryptic_hash_sha256_update().
 *
 * @param ctx Pointer to a SHA-256 context structure.
 */
void fossil_cryptic_hash_sha256_init(fossil_cryptic_hash_sha256_ctx_t *ctx);

/**
 * @brief Feed data into the SHA-256 context.
 *
 * This function can be called multiple times to process data in chunks.
 * The context must have been initialized with fossil_cryptic_hash_sha256_init().
 *
 * @param ctx  Pointer to a SHA-256 context structure.
 * @param data Pointer to the data to be hashed.
 * @param len  Number of bytes of data to hash.
 */
void fossil_cryptic_hash_sha256_update(fossil_cryptic_hash_sha256_ctx_t *ctx, const void *data, size_t len);

/**
 * @brief Finalize the SHA-256 hash computation and produce the digest.
 *
 * Once this function is called, the context should not be reused unless
 * reinitialized. It writes the final 256-bit hash to the output buffer.
 *
 * @param ctx Pointer to the SHA-256 context structure.
 * @param out Pointer to a 32-byte array for storing the digest.
 */
void fossil_cryptic_hash_sha256_final(fossil_cryptic_hash_sha256_ctx_t *ctx, uint8_t out[32]);

/**
 * @brief Convert a 32-byte SHA-256 digest to a hexadecimal string.
 *
 * Produces a null-terminated, lowercase hexadecimal string representation
 * of the given digest. The destination buffer must be at least 65 bytes long.
 *
 * @param hash Pointer to the 32-byte SHA-256 digest.
 * @param dest Pointer to a buffer where the resulting hex string will be stored.
 */
void fossil_cryptic_hash_sha256_to_hex(const uint8_t hash[32], char dest[65]);


/* ---------------------
 * One-shot hashing APIs
 * -------------------*/

/**
 * @brief Compute a CRC32 checksum (IEEE 802.3 standard) for the given buffer.
 *
 * @param data Pointer to the input data buffer.
 * @param len  Length of the input data in bytes.
 * @return 32-bit CRC32 checksum.
 */
uint32_t fossil_cryptic_hash_crc32(const void *data, size_t len);

/**
 * @brief Compute an FNV-1a 32-bit hash for the given buffer.
 *
 * @param data Pointer to the input data buffer.
 * @param len  Length of the input data in bytes.
 * @return 32-bit FNV-1a hash.
 */
uint32_t fossil_cryptic_hash_fnv1a32(const void *data, size_t len);

/**
 * @brief Compute an FNV-1a 64-bit hash for the given buffer.
 *
 * @param data Pointer to the input data buffer.
 * @param len  Length of the input data in bytes.
 * @return 64-bit FNV-1a hash.
 */
uint64_t fossil_cryptic_hash_fnv1a64(const void *data, size_t len);

/**
 * @brief Compute a MurmurHash3 x86_32 hash for the given buffer.
 *
 * Uses the public-domain reference implementation. This algorithm is
 * non-cryptographic but provides good distribution for general-purpose use.
 *
 * @param data Pointer to the input data buffer.
 * @param len  Length of the input data in bytes.
 * @param seed Seed value to influence the hash result.
 * @return 32-bit MurmurHash3 value.
 */
uint32_t fossil_cryptic_hash_murmur3_32(const void *data, size_t len, uint32_t seed);


/* ---------------------
 * Streaming API (init/update/final)
 * Supports CRC32, FNV-1a (32/64), and SHA-256
 * -------------------*/

/**
 * @brief Initialize a generic hashing context for the specified algorithm.
 *
 * @param ctx Pointer to a generic hash context.
 * @param alg Algorithm identifier (from fossil_cryptic_hash_alg_t).
 */
void fossil_cryptic_hash_init(fossil_cryptic_hash_ctx_t *ctx, fossil_cryptic_hash_alg_t alg);

/**
 * @brief Feed data into a generic streaming hash context.
 *
 * This function may be called multiple times. The algorithm is determined
 * by the context initialization.
 *
 * @param ctx  Pointer to the generic hash context.
 * @param data Pointer to the input data to hash.
 * @param len  Length of the input data in bytes.
 */
void fossil_cryptic_hash_update(fossil_cryptic_hash_ctx_t *ctx, const void *data, size_t len);

/**
 * @brief Finalize and obtain a 32-bit hash result.
 *
 * Valid for algorithms that produce 32-bit results (CRC32, FNV1A32, Murmur3).
 *
 * @param ctx Pointer to the generic hash context.
 * @return 32-bit hash result.
 */
uint32_t fossil_cryptic_hash_final32(fossil_cryptic_hash_ctx_t *ctx);

/**
 * @brief Finalize and obtain a 64-bit hash result.
 *
 * Only valid if ctx->alg == FOSSIL_CRYPTIC_HASH_ALG_FNV1A64.
 *
 * @param ctx Pointer to the generic hash context.
 * @return 64-bit hash result.
 */
uint64_t fossil_cryptic_hash_final64(fossil_cryptic_hash_ctx_t *ctx);

/**
 * @brief Finalize a streaming SHA-256 computation from a generic context.
 *
 * This is the SHA-256 variant of the generic streaming API. It extracts
 * the final 256-bit digest into the output buffer.
 *
 * @param ctx Pointer to the generic hash context.
 * @param out Pointer to a 32-byte buffer for the resulting digest.
 */
void fossil_cryptic_hash_final_sha256(fossil_cryptic_hash_ctx_t *ctx, uint8_t out[32]);

/**
 * @brief Convert a 32-bit hash value to a lowercase hexadecimal string.
 *
 * The output buffer must be at least 9 bytes (8 hex digits + null terminator).
 *
 * @param h    32-bit hash value.
 * @param dest Pointer to the destination buffer.
 */
void fossil_cryptic_hash_u32_to_hex(uint32_t h, char dest[9]);

/**
 * @brief Convert a 64-bit hash value to a lowercase hexadecimal string.
 *
 * The output buffer must be at least 17 bytes (16 hex digits + null terminator).
 *
 * @param h    64-bit hash value.
 * @param dest Pointer to the destination buffer.
 */
void fossil_cryptic_hash_u64_to_hex(uint64_t h, char dest[17]);

#ifdef __cplusplus
}
#include <string>
#include <vector>
#include <array>
#include <cstdint>
#include <cstring>

namespace fossil {

    namespace cryptic {

        /**
         * @brief C++ wrapper for one-shot and streaming hashing functions.
         *
         * This class provides both static one-shot hashing methods and
         * an object-oriented streaming interface that wraps the C API.
         */
        class Hash {
        public:
            /// Supported algorithms
            enum class Algorithm : uint32_t {
                CRC32       = FOSSIL_CRYPTIC_HASH_ALG_CRC32,
                FNV1a32     = FOSSIL_CRYPTIC_HASH_ALG_FNV1A32,
                FNV1a64     = FOSSIL_CRYPTIC_HASH_ALG_FNV1A64,
                Murmur3_32  = FOSSIL_CRYPTIC_HASH_ALG_MURMUR3_32,
                SHA256      = FOSSIL_CRYPTIC_HASH_ALG_SHA256
            };
        
            /// Size of SHA-256 digest in bytes
            static constexpr size_t SHA256_SIZE = 32;
        
            /// Default constructor (no algorithm set)
            Hash() = default;
        
            /**
             * @brief Construct and initialize with a specific algorithm.
             * @param alg Algorithm to use.
             */
            explicit Hash(Algorithm alg) { init(alg); }
        
            /**
             * @brief Initialize the hash context for a given algorithm.
             * @param alg Algorithm to use.
             */
            void init(Algorithm alg) {
                fossil_cryptic_hash_init(&ctx_, static_cast<fossil_cryptic_hash_alg_t>(alg));
            }
        
            /**
             * @brief Feed data into the hash computation.
             * @param data Pointer to bytes.
             * @param len Number of bytes.
             */
            void update(const void* data, size_t len) {
                fossil_cryptic_hash_update(&ctx_, data, len);
            }
        
            /**
             * @brief Finalize and return a 32-bit result.
             * @return 32-bit hash.
             */
            uint32_t final32() {
                return fossil_cryptic_hash_final32(&ctx_);
            }
        
            /**
             * @brief Finalize and return a 64-bit result.
             * @return 64-bit hash.
             */
            uint64_t final64() {
                return fossil_cryptic_hash_final64(&ctx_);
            }
        
            /**
             * @brief Finalize SHA-256 and return the digest as a byte array.
             * @return std::array<uint8_t, 32> containing the digest.
             */
            std::array<uint8_t, SHA256_SIZE> finalSHA256() {
                std::array<uint8_t, SHA256_SIZE> out{};
                fossil_cryptic_hash_final_sha256(&ctx_, out.data());
                return out;
            }
        
            /**
             * @brief Convert a 32-bit hash value to a lowercase hex string.
             */
            static std::string to_hex(uint32_t h) {
                char buf[9];
                fossil_cryptic_hash_u32_to_hex(h, buf);
                return std::string(buf);
            }
        
            /**
             * @brief Convert a 64-bit hash value to a lowercase hex string.
             */
            static std::string to_hex(uint64_t h) {
                char buf[17];
                fossil_cryptic_hash_u64_to_hex(h, buf);
                return std::string(buf);
            }
        
            /**
             * @brief Convert a SHA-256 digest to lowercase hex.
             */
            static std::string to_hex(const std::array<uint8_t, SHA256_SIZE>& digest) {
                char buf[65];
                fossil_cryptic_hash_sha256_to_hex(digest.data(), buf);
                return std::string(buf);
            }

            //
            /**
             * @brief Convert a SHA-256 digest (vector) to lowercase hex.
             */
            static std::string to_hex(const std::vector<uint8_t>& digest) {
                char buf[65];
                if (digest.size() != SHA256_SIZE) return {};
                fossil_cryptic_hash_sha256_to_hex(digest.data(), buf);
                return std::string(buf);
            }
        
            /**
             * @brief One-shot SHA-256.
             */
            static std::array<uint8_t, SHA256_SIZE> sha256(const void* data, size_t len) {
                std::array<uint8_t, SHA256_SIZE> out{};
                fossil_cryptic_hash_sha256(data, len, out.data());
                return out;
            }
        
            /**
             * @brief One-shot SHA-256, hex output.
             */
            static std::string sha256Hex(const void* data, size_t len) {
                auto digest = sha256(data, len);
                return to_hex(digest);
            }
        
            /**
             * @brief One-shot CRC32.
             */
            static uint32_t crc32(const void* data, size_t len) {
                return fossil_cryptic_hash_crc32(data, len);
            }
        
            /**
             * @brief One-shot FNV-1a 32-bit.
             */
            static uint32_t fnv1a32(const void* data, size_t len) {
                return fossil_cryptic_hash_fnv1a32(data, len);
            }
        
            /**
             * @brief One-shot FNV-1a 64-bit.
             */
            static uint64_t fnv1a64(const void* data, size_t len) {
                return fossil_cryptic_hash_fnv1a64(data, len);
            }
        
            /**
             * @brief One-shot MurmurHash3 x86_32.
             */
            static uint32_t murmur3_32(const void* data, size_t len, uint32_t seed) {
                return fossil_cryptic_hash_murmur3_32(data, len, seed);
            }
        
        private:
            fossil_cryptic_hash_ctx_t ctx_{};
        };

    } // namespace cryptic

} // namespace fossil

#endif

#endif /* FOSSIL_CRYPTIC_HASH_H */

SAMPLE CODE C #

#include <fossil/cryptic/hash.h>
#include <stdio.h>

int main() {
    const char* msg = "Hello, Fossil!";
    
    // --- One-shot hashing ---
    uint32_t crc = fossil_cryptic_hash_crc32(msg, 13);
    printf("CRC32: %08x\n", crc);

    uint8_t sha[32];
    fossil_cryptic_hash_sha256(msg, 13, sha);

    char hex[65];
    fossil_cryptic_hash_sha256_to_hex(sha, hex);
    printf("SHA-256: %s\n", hex);

    // --- Streaming hashing ---
    fossil_cryptic_hash_ctx_t ctx;
    fossil_cryptic_hash_init(&ctx, FOSSIL_CRYPTIC_HASH_ALG_SHA256);
    fossil_cryptic_hash_update(&ctx, msg, 5);  // partial
    fossil_cryptic_hash_update(&ctx, msg+5, 8); // remaining
    fossil_cryptic_hash_final_sha256(&ctx, sha);
    fossil_cryptic_hash_sha256_to_hex(sha, hex);
    printf("SHA-256 (streaming): %s\n", hex);

    return 0;
}

SAMPLE CODE C++ #

#include <fossil/cryptic/hash.h>
#include <iostream>
#include <string>
#include <array>

int main() {
    using namespace fossil::cryptic;

    std::string msg = "Hello, Fossil!";

    // --- One-shot hashing ---
    uint32_t crc = Hash::crc32(msg.data(), msg.size());
    std::cout << "CRC32: " << Hash::to_hex(crc) << "\n";

    auto sha = Hash::sha256(msg.data(), msg.size());
    std::cout << "SHA-256: " << Hash::to_hex(sha) << "\n";

    // --- Streaming hashing ---
    Hash h(Hash::Algorithm::SHA256);
    h.update(msg.data(), 5);          // partial
    h.update(msg.data() + 5, 8);      // remaining
    auto sha_stream = h.finalSHA256();
    std::cout << "SHA-256 (streaming): " << Hash::to_hex(sha_stream) << "\n";

    // FNV-1a 64-bit one-shot
    uint64_t fnv64 = Hash::fnv1a64(msg.data(), msg.size());
    std::cout << "FNV-1a 64-bit: " << Hash::to_hex(fnv64) << "\n";

    return 0;
}

What are your feelings

Updated on August 30, 2025