Last active
October 27, 2024 20:59
-
-
Save TruncatedDinoSour/c9cf6e355bb285be9cdd08576158a05f to your computer and use it in GitHub Desktop.
BLAKE2s hashing algorithm from scratch in the C programming language.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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 contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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 contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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