otpcli/base32.c

101 lines
2.4 KiB
C
Raw Permalink Normal View History

2021-02-12 03:42:38 +01:00
/*
2021-02-13 00:05:40 +01:00
* otpcli - command-line interface for HOTP and TOTP
2021-02-12 03:42:38 +01:00
* Written in 2021 by Lucas
*
* To the extent possible under law, the author(s) have dedicated all
* copyright and related and neighboring rights to this software to the
* public domain worldwide. This software is distributed without any
* warranty.
*
* You should have received a copy of the CC0 Public Domain Dedication
* along with this software. If not, see
* <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#include <limits.h>
#include <stdint.h>
#include <string.h>
#include "base32.h"
#define B32_VALID(a) (('A' <= (a) && (a) <= 'Z') || ('2' <= (a) && (a) <= '7'))
#define B32_DECODE_CHAR(a) ((a) <= '7' ? (a) - '2' + 26 : (a) - 'A')
static int b32_last_chunk_length[] = { 0, -1, 1, -1, 2, 3, -1, 4 };
static void
b32_decode_chunk(unsigned char out[5], const char in[8])
{
uint64_t x = 0;
x |= (uint64_t)B32_DECODE_CHAR(in[0]) << 35;
x |= (uint64_t)B32_DECODE_CHAR(in[1]) << 30;
x |= (uint64_t)B32_DECODE_CHAR(in[2]) << 25;
x |= (uint64_t)B32_DECODE_CHAR(in[3]) << 20;
x |= (uint64_t)B32_DECODE_CHAR(in[4]) << 15;
x |= (uint64_t)B32_DECODE_CHAR(in[5]) << 10;
x |= (uint64_t)B32_DECODE_CHAR(in[6]) << 5;
x |= (uint64_t)B32_DECODE_CHAR(in[7]);
out[0] = (x >> 32) & 0xff;
out[1] = (x >> 24) & 0xff;
out[2] = (x >> 16) & 0xff;
out[3] = (x >> 8) & 0xff;
out[4] = x & 0xff;
}
int
b32_decoded_len(const char *s, size_t n)
{
const char *it, *t;
size_t r, eqc;
for (it = s; *it != '\0' && B32_VALID(*it); it++)
continue;
if (*it != '\0' && *it != '=')
return -1;
for (t = it; *t == '=' && t - it < 8; t++)
continue;
eqc = t - it;
if (*t != '\0' || t - it == 8 || b32_last_chunk_length[eqc] == -1)
return -1;
r = n / 8 * 5 + b32_last_chunk_length[eqc == 0 ? n % 8 : eqc];
return r > INT_MAX ? -1 : r;
}
int
b32_decode(unsigned char *out, size_t outlen, const char *in, size_t inlen)
{
unsigned char tout[5];
char tin[8];
size_t i, t, eqc, last_chunk;
t = inlen;
while (in[inlen - 1] == '=')
inlen--;
eqc = t - inlen;
last_chunk = b32_last_chunk_length[eqc == 0 ? inlen % 8 : eqc];
while (inlen >= 8 && outlen >= 5) {
b32_decode_chunk(out, in);
in += 8;
inlen -= 8;
out += 5;
outlen -= 5;
}
if (inlen > 0) {
if (outlen == 0)
return 0;
memcpy(tin, in, inlen);
memset(tin + inlen, 'A', sizeof(tin) - inlen);
b32_decode_chunk(tout, tin);
memcpy(out, tout, outlen);
}
return outlen >= last_chunk;
}