burkey.co /docs/libflint/sha256
index sha256
~/docs/libflint/sha256.md
libflint

Sha256

libflint·Utilities

FIPS 180-4 SHA-256 of a byte span the caller already chose. The digest is 32 raw bytes. Processing uses 512-bit blocks, 64 rounds, and a big-endian 64-bit length. Digest words are written big-endian.

This module does not allocate. It is not declared from lfcrypto.h. That header includes codecs and puzzle bytes, not this hash. Hex text, when a caller wants it, goes through hex_encode or hex_encode_into in lfcodec.h (already lowercase).

Usage#

Hash with one call, or stream chunks through a caller-owned LfSha256. lf_sha256_init of a NULL pointer returns without writing.

#include "lfsha256.h"

int main(void) {
    unsigned char digest[LF_SHA256_DIGEST_LEN];
    LfSha256 ctx;

    if (lf_sha256("abc", 3, digest) != 0) return 1;
    /* digest[0..3] is ba 78 16 bf; 32 raw bytes, not text */

    lf_sha256_init(&ctx);
    if (lf_sha256_update(&ctx, "ab", 2) != 0) return 1;
    if (lf_sha256_update(&ctx, "c", 1) != 0) return 1;
    if (lf_sha256_final(&ctx, digest) != 0) return 1;
    return 0;
}

The caller owns the LfSha256 object. Nothing inside it is allocated, and a bitwise copy copies the in-progress hash. Concurrent use of one context is not supported. Two contexts do not share state.

A successful lf_sha256_final finishes the context by setting block_len greater than LF_SHA256_BLOCK_LEN. Further lf_sha256_update or lf_sha256_final calls return -1 until lf_sha256_init. Those calls leave out unchanged when it is non-NULL, and they leave the finished context unchanged. lf_sha256_final with a NULL out returns -1 and does not finish or otherwise change the context.

On any failure, the output buffer and the context are left as they were. out must not overlap the context.

Structs#

LfSha256#

typedef struct {
    uint32_t state[8];
    uint64_t bit_len;
    unsigned char block[LF_SHA256_BLOCK_LEN];
    unsigned int block_len;
} LfSha256;

state is the chaining value. bit_len counts message bits accepted so far, including bytes still sitting in block. block_len is how many of those bytes are waiting (0 to 63 while hashing). After a successful final it is greater than 64, which marks the context finished.

#define LF_SHA256_DIGEST_LEN 32
#define LF_SHA256_BLOCK_LEN 64

Functions#

lf_sha256_init#

Stores the FIPS 180-4 initial hash value and clears the counters. A NULL ctx returns without writing. There is no failure code and no allocation. Calling it again restarts a finished context.

void lf_sha256_init(LfSha256 *ctx);

lf_sha256_update#

Appends n bytes. Returns 0 on success. Returns -1 if ctx is NULL, if data is NULL and n > 0, if the context is finished, or if n * 8 + bit_len would not fit in uint64_t. The length check happens before the context is modified. data NULL and n == 0 succeeds and adds no bytes. On failure the context is unchanged.

int lf_sha256_update(LfSha256 *ctx, const void *data, size_t n);

lf_sha256_final#

Pads as FIPS 180-4 specifies, appends the big-endian bit length, and writes 32 bytes to out. Returns 0 on success. Returns -1 if ctx or out is NULL, or if the context is already finished. On failure out is unchanged and the context is unchanged. Success finishes the context; only lf_sha256_init makes it usable again.

int lf_sha256_final(LfSha256 *ctx, unsigned char out[LF_SHA256_DIGEST_LEN]);

lf_sha256#

One-shot hash of n bytes, implemented with init, update, and final. Empty input is data NULL or n == 0. Returns 0 on success and -1 on the same failures as update and final, including a NULL out and a bit length that does not fit in uint64_t. On failure out is unchanged.

int lf_sha256(const void *data, size_t n, unsigned char out[LF_SHA256_DIGEST_LEN]);