cybersecurity · intermediate · ~15 min

Bounded base64 decoder

Bounds-safe base64 decoding with strict validation.

Challenge

Decode standard base64 into a fixed buffer, rejecting malformed input instead of trusting it — the safe foundation for handling tokens, certs, and payloads.

Task

Implement int b64_decode(const char *in, uint8_t *out, size_t cap) that decodes the standard-base64 string in into out, writing at most cap bytes.

Input

  • in: a NUL-terminated standard-base64 string (A-Za-z0-9+/ with = padding) the grader passes.
  • out: caller buffer of size cap.

Output

Returns the number of bytes written, or -1 if: in is NULL, its length is not a multiple of 4, it contains an invalid character, = padding appears before the end, or the decoded output would exceed cap.

Example

b64_decode("TWFu", out, 64)   ->   3, out = "Man"
b64_decode("TWE=", out, 64)   ->   2, out = "Ma"
b64_decode("TQ==", out, 64)   ->   1, out = "M"
b64_decode("", out, 64)       ->   0
b64_decode("TWF", out, 64)    ->  -1   (length not multiple of 4)
b64_decode("T@Fu", out, 64)   ->  -1   (invalid char)
b64_decode("TWFu", tiny2, 2)  ->  -1   (would overflow cap)
b64_decode(NULL, out, 64)     ->  -1

Edge cases

  • The empty string decodes to 0 bytes.
  • One = means the quad yields 2 bytes; two = means 1 byte.
  • Check out capacity before writing each byte.

Rules

  • Pure decode into the caller buffer — no I/O, no allocation.

Why this matters

Base64 is everywhere in security tooling — tokens, certs, payloads. A bounded decoder that rejects bad input is the safe foundation.

Input format

A NUL-terminated base64 string in, an output buffer out, and its capacity cap.

Output format

An int: bytes written, or -1 on NULL/bad length/invalid char/misplaced padding/overflow.

Constraints

Standard base64; reject malformed input; never write past cap; no I/O.

Starter code

#include <stdint.h>
#include <stddef.h>
int b64_decode(const char *in, uint8_t *out, size_t cap) {
    /* TODO */
    (void)in; (void)out; (void)cap;
    return -1;
}

Common mistakes

Not rejecting length % 4 != 0. Allowing data after padding. Writing past cap.

Edge cases to handle

Empty string → 0. One/two '=' padding. Overflow into a tiny buffer.

Complexity

O(n).

Background lessons

Up next

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.