Skip to content

Instantly share code, notes, and snippets.

@TruncatedDinoSour
Last active October 27, 2024 20:59
Show Gist options
  • Save TruncatedDinoSour/c9cf6e355bb285be9cdd08576158a05f to your computer and use it in GitHub Desktop.
Save TruncatedDinoSour/c9cf6e355bb285be9cdd08576158a05f to your computer and use it in GitHub Desktop.
BLAKE2s hashing algorithm from scratch in the C programming language.
/*
* This file is/was a part of the Vessel project. (https://git.ari.lt/ari/vessel)
*
* Copyright (C) 2024 Ari Archer <ari@ari.lt>
*
* Vessel is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Vessel is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Vessel. If not, see <https://www.gnu.org/licenses/>.
*/
#define BLAKE2S_ROTR32(a, b) \
((uint32_t)(a) >> (uint32_t)(b)) ^ \
((uint32_t)(a) << (uint32_t)((uint32_t)32 - (uint32_t)(b)))
static const uint32_t BLAKE2S_IV[BLAKE2S_STATE_SIZE] = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
static const uint8_t BLAKE2S_SIGMA[160] = {
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};
static const uint8_t BLAKE2S_HEX_DIGITS[] = "0123456789abcdef";
static inline void Blake2sCtx_mix(Blake2sCtx *ctx,
const uint8_t a,
const uint8_t b,
const uint8_t c,
const uint8_t d,
const uint8_t x,
const uint8_t y) {
ctx->v[a] = ctx->v[a] + ctx->v[b] + ctx->m[BLAKE2S_SIGMA[x]];
ctx->v[d] = BLAKE2S_ROTR32(ctx->v[d] ^ ctx->v[a], 16);
ctx->v[c] = ctx->v[c] + ctx->v[d];
ctx->v[b] = BLAKE2S_ROTR32(ctx->v[b] ^ ctx->v[c], 12);
ctx->v[a] = ctx->v[a] + ctx->v[b] + ctx->m[BLAKE2S_SIGMA[y]];
ctx->v[d] = BLAKE2S_ROTR32(ctx->v[d] ^ ctx->v[a], 8);
ctx->v[c] = ctx->v[c] + ctx->v[d];
ctx->v[b] = BLAKE2S_ROTR32(ctx->v[b] ^ ctx->v[c], 7);
}
static void Blake2sCtx_compress(Blake2sCtx *ctx, const Bool is_last) {
uint8_t idx;
for (idx = 0; idx < 8; ++idx) {
ctx->v[idx] = ctx->state[idx];
ctx->v[idx + 8] = BLAKE2S_IV[idx];
}
ctx->v[12] ^= (uint32_t)(ctx->inputs & 0xFFFFFFFF);
ctx->v[13] ^= (uint32_t)(ctx->inputs >> 32);
/* last block flag */
if (is_last)
ctx->v[14] = ~ctx->v[14];
for (idx = 0; idx < 16; ++idx) {
const uint8_t pdx = idx * 4;
ctx->m[idx] = (uint32_t)ctx->input[pdx] ^
(uint32_t)((uint32_t)ctx->input[pdx + 1] << 8) ^
(uint32_t)((uint32_t)ctx->input[pdx + 2] << 16) ^
(uint32_t)((uint32_t)ctx->input[pdx + 3] << 24);
}
/* 10 rounds of mixing */
for (idx = 0; idx < 10; ++idx) {
Blake2sCtx_mix(ctx, 0, 4, 8, 12, (uint8_t)(idx * 16 + 0),
(uint8_t)(idx * 16 + 1));
Blake2sCtx_mix(ctx, 1, 5, 9, 13, (uint8_t)(idx * 16 + 2),
(uint8_t)(idx * 16 + 3));
Blake2sCtx_mix(ctx, 2, 6, 10, 14, (uint8_t)(idx * 16 + 4),
(uint8_t)(idx * 16 + 5));
Blake2sCtx_mix(ctx, 3, 7, 11, 15, (uint8_t)(idx * 16 + 6),
(uint8_t)(idx * 16 + 7));
Blake2sCtx_mix(ctx, 0, 5, 10, 15, (uint8_t)(idx * 16 + 8),
(uint8_t)(idx * 16 + 9));
Blake2sCtx_mix(ctx, 1, 6, 11, 12, (uint8_t)(idx * 16 + 10),
(uint8_t)(idx * 16 + 11));
Blake2sCtx_mix(ctx, 2, 7, 8, 13, (uint8_t)(idx * 16 + 12),
(uint8_t)(idx * 16 + 13));
Blake2sCtx_mix(ctx, 3, 4, 9, 14, (uint8_t)(idx * 16 + 14),
(uint8_t)(idx * 16 + 15));
}
/* finalize the compression */
for (idx = 0; idx < 8; ++idx)
ctx->state[idx] ^= ctx->v[idx] ^ ctx->v[idx + 8];
}
Bool Blake2sCtx_init(Blake2sCtx *ctx,
const uint8_t outlen,
const void *key,
const uint8_t keylen) {
uint8_t idx;
if (!ctx || (key && (keylen < 1 || keylen > 32)))
return False;
for (idx = 0; idx < BLAKE2S_STATE_SIZE; ++idx)
ctx->state[idx] = BLAKE2S_IV[idx];
for (idx = 0; idx < BLAKE2S_INPUT_SIZE; ++idx)
ctx->input[idx] = 0;
for (idx = 0; idx < 16; ++idx)
ctx->v[idx] = ctx->m[idx] = 0;
ctx->idx = 0;
ctx->inputs = 0;
ctx->outlen = outlen;
ctx->state[0] ^= (uint32_t)0x01010000 ^ (uint32_t)((uint32_t)keylen << 8) ^
(uint32_t)outlen;
if (key) {
Blake2sCtx_update(ctx, key, keylen);
ctx->idx = BLAKE2S_INPUT_SIZE;
}
return True;
}
Bool Blake2sCtx_update(Blake2sCtx *ctx,
const void *data,
const uint64_t datalen) {
if (!ctx || !data || datalen < 1)
return False;
for (uint64_t idx = 0; idx < datalen; ++idx) {
if (ctx->idx == BLAKE2S_INPUT_SIZE) {
ctx->inputs += ctx->idx;
Blake2sCtx_compress(ctx, False);
ctx->idx = 0;
}
ctx->input[ctx->idx++] = ((const uint8_t *)data)[idx];
}
return True;
}
Bool Blake2sCtx_final(Blake2sCtx *ctx) {
ctx->inputs += ctx->idx;
while (ctx->idx < 64)
ctx->input[ctx->idx++] = 0;
Blake2sCtx_compress(ctx, True);
for (uint8_t idx = 0; idx < ctx->outlen; ++idx)
ctx->digest[idx] = (ctx->state[idx >> 2] >> (8 * (idx & 3))) & 0xff;
return True;
}
Bool Blake2sCtx_to_hex(Blake2sCtx *ctx, void *out) {
uint8_t idx;
uint8_t *data = (uint8_t *)out;
if (!ctx || !out)
return False;
for (idx = 0; idx < ctx->outlen; ++idx) {
data[idx * 2] = BLAKE2S_HEX_DIGITS[ctx->digest[idx] >> 4];
data[idx * 2 + 1] = BLAKE2S_HEX_DIGITS[ctx->digest[idx] & 0xF];
}
data[idx * 2] = '\0';
return True;
}
/*
* This file is/was a part of the Vessel project. (https://git.ari.lt/ari/vessel)
*
* Copyright (C) 2024 Ari Archer <ari@ari.lt>
*
* Vessel is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Vessel is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Vessel. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef _VESSEL_BLAKE2S_H
#define _VESSEL_BLAKE2S_H
#include "def.h"
#define BLAKE2S_STATE_SIZE 8
#define BLAKE2S_INPUT_SIZE 64
#define BLAKE2S_OUTPUT_SIZE_MAX 32
#define PRIxBLAKE2s256 "%016jx%016jx%016jx%016jx"
#define BLAKE2s256(ctx) \
*(const uint64_t *)((ctx).digest), *(const uint64_t *)((ctx).digest + 8), \
*(const uint64_t *)((ctx).digest + 16), \
*(const uint64_t *)((ctx).digest + 24)
typedef struct {
uint32_t state[BLAKE2S_STATE_SIZE]; /* Hash state */
uint8_t input[BLAKE2S_INPUT_SIZE]; /* Input block */
uint8_t idx; /* Index within the input block */
uint64_t inputs; /* Input count */
uint8_t outlen; /* Output length */
uint32_t v[16], m[16]; /* Block information */
uint8_t digest[BLAKE2S_OUTPUT_SIZE_MAX]; /* Output hash */
} Blake2sCtx;
Bool Blake2sCtx_init(Blake2sCtx *ctx,
const uint8_t outlen,
const void *key,
const uint8_t keylen);
Bool Blake2sCtx_update(Blake2sCtx *ctx,
const void *data,
const uint64_t datalen);
Bool Blake2sCtx_final(Blake2sCtx *ctx);
Bool Blake2sCtx_to_hex(Blake2sCtx *ctx, void *out);
#endif /* _VESSEL_BLAKE2S_H */
/*
* This file was a part of the Vessel project. (https://git.ari.lt/ari/vessel)
*
* Copyright (C) 2024 Ari Archer <ari@ari.lt>
*
* Vessel is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Vessel is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Vessel. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef _VESSEL_DEF_H
#define _VESSEL_DEF_H
#include <wchar.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#ifndef NULL
#define NULL ((void *)0)
#endif /* NULL */
typedef uint8_t Bool;
#define False ((Bool)0)
#define True ((Bool)1)
char *dupnstr(const char *src, const uint64_t len);
#define dupstr(str) dupnstr((str), strlen((str)))
wchar_t *wdupnstr(const wchar_t *src, const uint64_t len);
#define wdupstr(str) wdupnstr((str), wlenstr((str)))
uint64_t wlenstr(const wchar_t *str);
#define Min(a, b) (((a) > (b)) ? (b) : (a))
#define Max(a, b) (((a) < (b)) ? (b) : (a))
char *lowstr(char *src);
char *uppstr(char *src);
Bool startstr(const char *src, const char *prefix, const Bool sensitive);
#endif /* _VESSEL_DEF_H */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment