Last active
August 19, 2024 01:33
-
-
Save jkiv/5efcdd0e378c59a864be87adaec35957 to your computer and use it in GitHub Desktop.
Base32 Decoder in C.
This file contains hidden or 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
#include <stdio.h> | |
#include <stdlib.h> | |
char b32_decode_char(char c); | |
void b32_decode(char** dst, size_t* dstlen, const char* src, size_t srclen); | |
void b32_decode(char** dst, size_t* dstlen, const char* src, size_t srclen) | |
{ | |
size_t padlen = 0; // Number of ='s in padding | |
size_t lastlen = 0; // Length of last quantum in characters | |
*dst = NULL; | |
*dstlen = 0; | |
// Check padding | |
for (size_t i = 1; i < srclen; i++) | |
{ | |
if (src[srclen-i] == '=') | |
padlen++; | |
else | |
break; | |
} | |
// Check source material | |
for (size_t i = 0; i < srclen - padlen; i++) | |
{ | |
if (b32_decode_char(src[i]) > 0x1F) | |
{ | |
// ERROR: one or more characters cannot be decoded | |
return; | |
} | |
} | |
// Calculate the length of the last quantum in src | |
lastlen = (srclen - padlen) % 8; | |
// How many quantums do we have? | |
size_t qmax = (srclen - padlen) / 8; | |
if (lastlen > 0) | |
{ | |
// Last quantum is a partial quantum | |
// ... qmax rounded down | |
qmax += 1; | |
} | |
else | |
{ | |
// Last quantum is a full quantum | |
// ... length of last quantum is 8, not 0 | |
lastlen = 8; | |
} | |
// Calculate dst buffer size | |
*dstlen = ((srclen - padlen) / 8) * 5; | |
switch(lastlen) | |
{ | |
case 8: | |
break; | |
case 7: | |
*dstlen += 4; | |
break; | |
case 5: | |
*dstlen += 3; | |
break; | |
case 4: | |
*dstlen += 2; | |
break; | |
case 2: | |
*dstlen += 1; | |
break; | |
default: | |
// ERROR: Not a multiple of a byte. | |
*dstlen = 0; | |
break; | |
} | |
if (dstlen == 0) | |
{ | |
// Either empty src, or an error occurred | |
return; | |
} | |
// Allocate dst buffer | |
*dst = (char*) malloc(sizeof(char) * (*dstlen)); | |
// Loop variables | |
size_t qlen; | |
char* pdst = *dst; | |
const char* psrc = src; | |
// Decode each quantum | |
for (size_t q = 0; q < qmax; q++) | |
{ | |
// Are we on the last quantum? | |
if (q == qmax - 1) | |
qlen = lastlen; | |
else | |
qlen = 8; | |
// dst 0 1 2 3 4 | |
// [11111 111][11 11111 1][1111 1111][1 11111 11][111 11111] | |
// src 0 1 2 3 4 5 6 7 | |
switch(qlen) | |
{ | |
// 8 = 5 bytes in quantum | |
case 8: | |
pdst[4] = b32_decode_char(psrc[7]); | |
pdst[4] |= b32_decode_char(psrc[6]) << 5; | |
// 7 = 4 bytes in quantum | |
case 7: | |
pdst[3] = b32_decode_char(psrc[6]) >> 3; | |
pdst[3] |= (b32_decode_char(psrc[5]) & 0x1F) << 2; | |
pdst[3] |= b32_decode_char(psrc[4]) << 7; | |
// 5 = 3 bytes in quantum | |
case 5: | |
pdst[2] = b32_decode_char(psrc[4]) >> 1; | |
pdst[2] |= b32_decode_char(psrc[3]) << 4; | |
// 4 = 2 bytes in quantum | |
case 4: | |
pdst[1] = b32_decode_char(psrc[3]) >> 4; | |
pdst[1] |= (b32_decode_char(psrc[2]) & 0x1F) << 1; | |
pdst[1] |= b32_decode_char(psrc[1]) << 6; | |
// 2 = 1 byte in quantum | |
case 2: | |
pdst[0] = b32_decode_char(psrc[1]) >> 2; | |
pdst[0] |= b32_decode_char(psrc[0]) << 3; | |
break; | |
default: | |
break; // TODO error | |
} | |
// Move quantum pointers forward | |
psrc += 8; | |
pdst += 5; | |
} | |
} | |
char b32_decode_char(char c) | |
{ | |
if (c >= 'A' && c <= 'Z') | |
return c - 'A'; | |
else if (c >= '2' && c <= '7') | |
return c - '2' + 26; | |
// ... handle lowercase here??? | |
else if (c >= 'a' && c <= 'z') | |
return c - 'a'; | |
else | |
return 0xFF; // ERROR | |
} | |
int test_b32_decode(char* src, size_t srclen, char* expected, size_t expectedlen) | |
{ | |
fprintf(stderr, "Testing b32_decode case '%s' (%i)... ", src, srclen); | |
// Usage: | |
char* dst; | |
size_t dstlen; | |
b32_decode(&dst, &dstlen, src, srclen); | |
// -- now we own dst | |
if (dstlen != expectedlen) | |
{ | |
fprintf(stderr, "FAILED\n\tDecoded length is %i, expecting %i.\n", dstlen, expectedlen); | |
free(dst); | |
exit(1); | |
} | |
for (int i = 0; i < dstlen; i++) | |
{ | |
if (dst[i] != expected[i]) | |
{ | |
fprintf(stderr, "FAILED\n\tResult byte %i differs from expected.\n\t\tgot:\t%02X\n\t\texpecting:\t%02X\n", i, dst[i], expected[i]); | |
free(dst); | |
exit(1); | |
} | |
} | |
if (dst != NULL && dstlen != 0) | |
free(dst); | |
fprintf(stderr, " PASSED\n"); | |
} | |
int main() | |
{ | |
// Test vectors | |
test_b32_decode("MY======", 8, "f", 1); | |
test_b32_decode("MZXQ====", 8, "fo", 2); | |
test_b32_decode("MZXW6===", 8, "foo", 3); | |
test_b32_decode("MZXW6YQ=", 8, "foob", 4); | |
test_b32_decode("MZXW6YTB", 8, "fooba", 5); | |
test_b32_decode("MZXW6YTBOI======", 16, "foobar", 6); | |
test_b32_decode("MY", 2, "f", 1); | |
test_b32_decode("MZXQ", 4, "fo", 2); | |
test_b32_decode("MZXW6", 5, "foo", 3); | |
test_b32_decode("MZXW6YQ", 7, "foob", 4); | |
test_b32_decode("MZXW6YTB", 8, "fooba", 5); | |
test_b32_decode("MZXW6YTBOI", 10, "foobar", 6); | |
exit(0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment