Skip to content

File syn_blake2s.c

File List > crypto > syn_blake2s.c

Go to the documentation of this file

#if __has_include("syn_config.h")
#include "syn_config.h"
#endif

#if !defined(SYN_USE_BLAKE2S) || SYN_USE_BLAKE2S

#include "../util/syn_pack.h"
#include "syn_blake2s.h"

#include <string.h>

/* ── IV (same as SHA-256, from the fractional parts of sqrt(2..19)) ───── */

static const uint32_t blake2s_iv[8] = {
    0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
    0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL,
};

/* ── Message word permutation schedule (10 rounds) ────────────────────── */

static const uint8_t sigma[10][16] = {
    {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
    {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
    {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
    {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
    {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
    {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
    {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
    {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
    {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
    {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0},
};

/* ── Helpers ────────────────────────────────────────────────────────────── */

static inline uint32_t rotr32(uint32_t x, unsigned n)
{
    return (x >> n) | (x << (32 - n));
}

static inline uint32_t load32_le(const uint8_t *p)
{
    return syn_peek_u32_le(p, 0);
}

static inline void store32_le(uint8_t *p, uint32_t v)
{
    syn_poke_u32_le(v, p, 0);
}

/* ── G mixing function ──────────────────────────────────────────────────── */

#define G(v, a, b, c, d, x, y)          \
    do {                                \
        v[a] += v[b] + (x);             \
        v[d] = rotr32(v[d] ^ v[a], 16); \
        v[c] += v[d];                   \
        v[b] = rotr32(v[b] ^ v[c], 12); \
        v[a] += v[b] + (y);             \
        v[d] = rotr32(v[d] ^ v[a], 8);  \
        v[c] += v[d];                   \
        v[b] = rotr32(v[b] ^ v[c], 7);  \
    } while (0)

/* ── Compression function ───────────────────────────────────────────────── */

static void blake2s_compress(SYN_BLAKE2s *ctx, const uint8_t block[64], int is_last)
{
    uint32_t v[16];
    uint32_t m[16];
    unsigned i;

    /* Load message words */
    for (i = 0; i < 16; i++) {
        m[i] = load32_le(block + i * 4);
    }

    /* Initialize working vector */
    for (i = 0; i < 8; i++) {
        v[i] = ctx->h[i];
        v[i + 8] = blake2s_iv[i];
    }

    v[12] ^= ctx->t[0];
    v[13] ^= ctx->t[1];

    if (is_last) {
        v[14] ^= 0xFFFFFFFFUL;
    }

    /* 10 rounds of mixing */
    for (i = 0; i < 10; i++) {
        const uint8_t *s = sigma[i];

        /* Column step */
        G(v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
        G(v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
        G(v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
        G(v, 3, 7, 11, 15, m[s[6]], m[s[7]]);

        /* Diagonal step */
        G(v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
        G(v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
        G(v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
        G(v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
    }

    /* Finalize */
    for (i = 0; i < 8; i++) {
        ctx->h[i] ^= v[i] ^ v[i + 8];
    }
}

/* ── Increment byte counter ─────────────────────────────────────────────── */

static inline void blake2s_increment_counter(SYN_BLAKE2s *ctx, uint32_t inc)
{
    ctx->t[0] += inc;
    if (ctx->t[0] < inc) {
        ctx->t[1]++;
    }
}

/* ── Public API ─────────────────────────────────────────────────────────── */

void syn_blake2s_init(SYN_BLAKE2s *ctx, size_t outlen)
{
    unsigned i;

    memset(ctx, 0, sizeof(*ctx));

    for (i = 0; i < 8; i++) {
        ctx->h[i] = blake2s_iv[i];
    }

    /* Parameter block: fanout=1, depth=1, digest_length=outlen */
    ctx->h[0] ^= 0x01010000UL ^ (uint32_t)outlen;
    ctx->outlen = (uint8_t)outlen;
}

void syn_blake2s_init_keyed(SYN_BLAKE2s *ctx, const void *key, size_t keylen, size_t outlen)
{
    uint8_t block[SYN_BLAKE2S_BLOCK_SIZE];
    unsigned i;

    memset(ctx, 0, sizeof(*ctx));

    for (i = 0; i < 8; i++) {
        ctx->h[i] = blake2s_iv[i];
    }

    /* Parameter block: fanout=1, depth=1, keylen, digest_length */
    ctx->h[0] ^= 0x01010000UL ^ ((uint32_t)keylen << 8) ^ (uint32_t)outlen;
    ctx->outlen = (uint8_t)outlen;

    /* Key is the first block, zero-padded */
    memset(block, 0, sizeof(block));
    memcpy(block, key, keylen);
    syn_blake2s_update(ctx, block, SYN_BLAKE2S_BLOCK_SIZE);
}

void syn_blake2s_update(SYN_BLAKE2s *ctx, const void *data, size_t len)
{
    const uint8_t *in = (const uint8_t *)data;

    while (len > 0) {
        /* If buffer is full, compress it (it's not the last block
         * because we still have more data) */
        if (ctx->buflen == SYN_BLAKE2S_BLOCK_SIZE) {
            blake2s_increment_counter(ctx, SYN_BLAKE2S_BLOCK_SIZE);
            blake2s_compress(ctx, ctx->buf, 0);
            ctx->buflen = 0;
        }

        size_t fill = SYN_BLAKE2S_BLOCK_SIZE - ctx->buflen;
        if (fill > len)
            fill = len;

        memcpy(ctx->buf + ctx->buflen, in, fill);
        ctx->buflen += (uint8_t)fill;
        in += fill;
        len -= fill;
    }
}

void syn_blake2s_final(SYN_BLAKE2s *ctx, uint8_t *out)
{
    unsigned i;

    blake2s_increment_counter(ctx, (uint32_t)ctx->buflen);

    /* Zero-pad remaining buffer */
    memset(ctx->buf + ctx->buflen, 0, SYN_BLAKE2S_BLOCK_SIZE - ctx->buflen);

    /* Compress final block */
    blake2s_compress(ctx, ctx->buf, 1);

    /* Extract output */
    for (i = 0; i < ctx->outlen / 4; i++) {
        store32_le(out + i * 4, ctx->h[i]);
    }

    /* Handle non-word-aligned output length */
    if (ctx->outlen & 3) {
        uint8_t tmp[4];
        store32_le(tmp, ctx->h[ctx->outlen / 4]);
        memcpy(out + (ctx->outlen & ~3u), tmp, ctx->outlen & 3);
    }
}

#endif /* SYN_USE_BLAKE2S */