initial commit
[authenticator.git] / base64.c
1 /* Copyright 2015 Jann Horn <jann@thejh.net>.
2  * See LICENSE for license information.
3  * 
4  * This file performs Base64 encoding and decoding using the 
5  * "URL and Filename Safe Alphabet" from RFC 4648; however, it does
6  * not attempt to be RFC-compliant apart from the choice of alphabet.
7  * These Base64 routines are not generic; they only convert between
8  * 24 bits of binary data and 4 characters of Base64.
9  */
10
11 #define _GNU_SOURCE
12 #include <stdint.h>
13 #include <assert.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17
18 static char alphabet[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
19
20 static char bin_to_char(uint32_t n) {
21   return alphabet[n];
22 }
23
24 static uint32_t char_to_bin(char c) {
25   char *p = strchr(alphabet, c);
26   if (p == 0) {
27     fprintf(stderr, "invalid base64 character encountered: '%c'\n", c);
28     exit(1);
29   }
30   return p - alphabet;
31 }
32
33 /* base64-encode 24 bits of data, least significant bits first */
34 void base64_encode(uint32_t n, char out[5]) {
35   assert((n & 0xff000000U) == 0);
36   for (int i = 0; i < 4; i++) {
37     out[i] = bin_to_char(n & 0x3f);
38     n >>= 6;
39   }
40   out[4] = '\0';
41   assert(n == 0);
42 }
43
44 uint32_t base64_decode(char *in) {
45   size_t len = strlen(in);
46   if (len != 4) {
47     fprintf(stderr, "Expected 4 bytes of Base64 data, but got %d\n", (int)len);
48     exit(1);
49   }
50   uint32_t out = 0;
51   for (int i = 3; i >= 0; i--) {
52     out <<= 6;
53     out |= char_to_bin(in[i]);
54   }
55   assert((out & 0xff000000U) == 0);
56   return out;
57 }