Reimplementation of Pijul in C, for education, fun and absolutely no profit
#include <stdint.h>
#include <unistd.h>

#include "common.h"
#include "types.h"

/**
 * Base32 encoding/decoding - for now only supports presenting blake3 hashes (32
 * bytes).
 */

/**
 * Inspired by the base32 implementation here:
 *
 * https://sourceforge.net/p/cyoencode/code/HEAD/tree/trunk/src/CyoDecode.c
 *
 * Basically, the idea is to allocate a much too large array, fill it with 0xff
 * and plug in the values from the alphabet in their corresponding holes.
 */
/* clang-format off */
static u8 BASE32[0x80] = {
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
	0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 
	0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 
	0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
/* clang-format on */

/**
 * Known limitations:
 *
 * (1) processes exactly 53 characters, so not a general base32 decoder
 * (2) assumes that all characters are valid base32 characters
 * (3) no validation that input values are <= 0x80 (so out-of-bounds is
 * possible)
 * (4) does not validate the 33rd byte to be a "1" meaning blake3.
 */
void
b32dec(u8 *dst, char const *in)
{
	int i, r, off;
	u8 v[8];

	for (r = 0; r < 6; r++) {
		off = 8 * r;
		for (i = 0; i < 8; i++)
			v[i] = BASE32[(int)in[off + i]];

		off = 5 * r;
		dst[off] = v[0] << 3 | v[1] >> 2;
		dst[off + 1] = v[1] << 6 | v[2] << 1 | v[3] >> 4;
		dst[off + 2] = v[3] << 4 | v[4] >> 1;
		dst[off + 3] = v[4] << 7 | v[5] << 2 | v[6] >> 3;
		dst[off + 4] = v[6] << 5 | v[7];
	}
	/* decode the remaining two bytes */
	off = 48; /* 6×8 */
	for (i = 0; i < 5; i++)
		v[i] = BASE32[(int)in[off + i]];

	off = 30; /* 6×5 */
	dst[off] = v[0] << 3 | v[1] >> 2;
	dst[off + 1] = v[1] << 6 | v[2] << 1 | v[3] >> 4;
}

static const char BASE32_ENC[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=";

/**
 * Base32 encoding for hashes. This routine only handles (and assumes) inputs of
 * length 32. Additionally, it doesn't do any padding.
 *
 * The output should large enough to hold 53 character. One interesting feature
 * of Pijul's use of base32 is that the hash type is appended to the output as
 * an extra byte where 1 = BLAKE3. This is the reason that all blake3-hashed
 * change files end with "C".
 */
void
b32enc(char *dst, u8 const *hash)
{
	u8 b[5];
	int i, j;

	for (i = 0; i < 6; i++) {
		for (j = 0; j < 5; j++)
			b[j] = hash[i * 5 + j];
		*dst++ = BASE32_ENC[b[0] >> 3];
		*dst++ = BASE32_ENC[(b[0] & 0x7) << 2 | (b[1] & 0xc0) >> 6];
		*dst++ = BASE32_ENC[(b[1] & 0x3e) >> 1];
		*dst++ = BASE32_ENC[(b[1] & 0x1) << 4 | (b[2] & 0xf0) >> 4];
		*dst++ = BASE32_ENC[(b[2] & 0x0f) << 1 | (b[3] & 0x80) >> 7];
		*dst++ = BASE32_ENC[(b[3] & 0x7c) >> 2];
		*dst++ = BASE32_ENC[(b[3] & 0x3) << 3 | (b[4] & 0xe0) >> 5];
		*dst++ = BASE32_ENC[b[4] & 0x1f];
	}
	b[0] = hash[30];
	b[1] = hash[31];
	b[2] = 1; /* Blake3 */
	b[3] = b[4] = 0;

	*dst++ = BASE32_ENC[b[0] >> 3];
	*dst++ = BASE32_ENC[(b[0] & 0x7) << 2 | (b[1] & 0xc0) >> 6];
	*dst++ = BASE32_ENC[(b[1] & 0x3e) >> 1];
	*dst++ = BASE32_ENC[(b[1] & 0x1) << 4 | (b[2] & 0xf0) >> 4];
	*dst++ = BASE32_ENC[(b[2] & 0x0f) << 1 | (b[3] & 0x80) >> 7];
}