/* * otpcli - command-line interface for HOTP and TOTP * 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 * . */ #include #include #include #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; }