3 changed files with 104 additions and 1 deletions
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* otpcli - CLI utility for generating OTPs |
||||
* |
||||
* 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; |
||||
} |
Loading…
Reference in new issue