Commit 61aee192 authored by Rusty Russell's avatar Rusty Russell

str/base32: new module.

Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 1e888a93
../../../licenses/CC0
\ No newline at end of file
#include "config.h"
#include <stdio.h>
#include <string.h>
/**
* str/base32 - RFC4648 base32 encoder/decoder.
*
* This code implements RFC4638 encoding, but you should use bech32 for most
* things anyway.
*
* License: CC0 (Public domain)
* Author: Rusty Russell <rusty@rustcorp.com.au>
*/
int main(int argc, char *argv[])
{
/* Expect exactly one argument */
if (argc != 2)
return 1;
if (strcmp(argv[1], "depends") == 0) {
printf("ccan/endian\n");
return 0;
}
return 1;
}
/* CC0 license (public domain) - see LICENSE file for details */
#include "base32.h"
#include <assert.h>
#include <ccan/endian/endian.h>
#include <string.h> /* for memcpy, memset */
/* RFC 4648:
*
* (1) The final quantum of encoding input is an integral multiple of 40
* bits; here, the final unit of encoded output will be an integral
* multiple of 8 characters with no "=" padding.
*
* (2) The final quantum of encoding input is exactly 8 bits; here, the
* final unit of encoded output will be two characters followed by
* six "=" padding characters.
*
* (3) The final quantum of encoding input is exactly 16 bits; here, the
* final unit of encoded output will be four characters followed by
* four "=" padding characters.
*
* (4) The final quantum of encoding input is exactly 24 bits; here, the
* final unit of encoded output will be five characters followed by
* three "=" padding characters.
*
* (5) The final quantum of encoding input is exactly 32 bits; here, the
* final unit of encoded output will be seven characters followed by
* one "=" padding character.
*/
static size_t padlen(size_t remainder)
{
switch (remainder) {
case 0:
return 0;
case 1:
return 6;
case 2:
return 4;
case 3:
return 3;
case 4:
return 1;
default:
abort();
}
}
size_t base32_str_size(size_t bytes)
{
return (bytes + 4) / 5 * 8 + 1;
}
size_t base32_data_size(const char *str, size_t strlen)
{
/* 8 chars == 5 bytes, round up to avoid overflow even though
* not required for well-formed strings. */
size_t max = (strlen + 7) / 8 * 5, padding = 0;
/* Count trailing padding bytes. */
while (strlen && str[strlen-1] == '=' && padding < 6) {
strlen--;
padding++;
}
return max - (padding * 5 + 7) / 8;
}
static bool decode_8_chars(const char c[8], beint64_t *res, int *bytes)
{
uint64_t acc = 0;
size_t num_pad = 0;
for (int i = 0; i < 8; i++) {
uint8_t val;
acc <<= 5;
if (c[i] >= 'a' && c[i] <= 'z')
val = c[i] - 'a';
else if (c[i] >= 'A' && c[i] <= 'Z')
val = c[i] - 'A';
else if (c[i] >= '2' && c[i] <= '7')
val = c[i] - '2' + 26;
else if (c[i] == '=') {
num_pad++;
continue;
} else
return false;
/* Can't have padding then non-pad */
if (num_pad)
return false;
acc |= val;
}
*res = cpu_to_be64(acc);
/* Can't have 2 or 5 padding bytes */
if (num_pad == 5 || num_pad == 2)
return false;
*bytes = (40 - num_pad * 5) / 8;
return true;
}
bool base32_decode(const char *str, size_t slen, void *buf, size_t bufsize)
{
while (slen >= 8) {
beint64_t val;
int bytes;
if (!decode_8_chars(str, &val, &bytes))
return false;
str += 8;
slen -= 8;
/* Copy bytes into dst. */
if (bufsize < bytes)
return false;
memcpy(buf, (char *)&val + 3, bytes);
buf = (char *)buf + bytes;
bufsize -= bytes;
}
return slen == 0 && bufsize == 0;
}
static void encode_8_chars(char *dest, const uint8_t *buf, int bytes)
{
beint64_t val = 0;
uint64_t res;
int bits = bytes * 8;
static const char enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
assert(bytes > 0 && bytes <= 5);
memcpy((char *)&val + 3, buf, bytes);
res = be64_to_cpu(val);
while (bits > 0) {
*dest = enc[(res >> 35) & 0x1F];
dest++;
res <<= 5;
bits -= 5;
}
if (bytes != 5)
memset(dest, '=', padlen(bytes));
}
bool base32_encode(const void *buf, size_t bufsize, char *dest, size_t destsize)
{
while (bufsize) {
int bytes = 5;
if (bytes > bufsize)
bytes = bufsize;
if (destsize < 8)
return false;
encode_8_chars(dest, buf, bytes);
buf = (const char *)buf + bytes;
bufsize -= bytes;
destsize -= 8;
dest += 8;
}
if (destsize != 1)
return false;
*dest = '\0';
return true;
}
/* CC0 (Public domain) - see LICENSE file for details */
#ifndef CCAN_STR_BASE32_H
#define CCAN_STR_BASE32_H
#include "config.h"
#include <stdbool.h>
#include <stdlib.h>
/**
* base32_decode - Unpack a base32 string.
* @str: the base32 string
* @slen: the length of @str
* @buf: the buffer to write the data into
* @bufsize: the length of @buf
*
* Returns false if there are any characters which aren't valid encodings
* or the string wasn't the right length for @bufsize.
*
* Example:
* unsigned char data[20];
*
* if (!base32_decode(argv[1], strlen(argv[1]), data, 20))
* printf("String is malformed!\n");
*/
bool base32_decode(const char *str, size_t slen, void *buf, size_t bufsize);
/**
* base32_encode - Create a nul-terminated base32 string
* @buf: the buffer to read the data from
* @bufsize: the length of @buf
* @dest: the string to fill
* @destsize: the max size of the string
*
* Returns true if the string, including terminator, fits in @destsize;
*
* Example:
* unsigned char buf[] = { 'f', 'o' };
* char str[9];
*
* if (!base32_encode(buf, sizeof(buf), str, sizeof(str)))
* abort();
*/
bool base32_encode(const void *buf, size_t bufsize, char *dest, size_t destsize);
/**
* base32_str_size - Calculate how big a nul-terminated base32 string is
* @bytes: bytes of data to represent
*
* Example:
* unsigned char buf[] = { 'f', 'o' };
* char str[base32_str_size(sizeof(buf))];
*
* base32_encode(buf, sizeof(buf), str, sizeof(str));
*/
size_t base32_str_size(size_t bytes);
/**
* base32_data_size - Calculate how many bytes of data in a base32 string
* @str: the string
* @strlen: the length of str to examine.
*
* Example:
* const char str[] = "MZXQ====";
* unsigned char buf[base32_data_size(str, strlen(str))];
*
* base32_decode(str, strlen(str), buf, sizeof(buf));
*/
size_t base32_data_size(const char *str, size_t strlen);
#endif /* CCAN_STR_BASE32_H */
#include <ccan/str/base32/base32.h>
/* Include the C files directly. */
#include <ccan/str/base32/base32.c>
#include <ccan/tap/tap.h>
static void test(const char *data, const char *b32)
{
char test[1000];
ok1(base32_str_size(strlen(data)) == strlen(b32) + 1);
ok1(base32_data_size(b32, strlen(b32)) == strlen(data));
ok1(base32_encode(data, strlen(data), test, strlen(b32)+1));
ok1(strcmp(test, b32) == 0);
test[strlen(data)] = '\0';
ok1(base32_decode(b32, strlen(b32), test, strlen(data)));
ok1(strcmp(test, data) == 0);
}
int main(void)
{
/* This is how many tests you plan to run */
plan_tests(8 * 6);
/* Test vectors from RFC */
test("", "");
test("f", "MY======");
test("fo", "MZXQ====");
test("foo", "MZXW6===");
test("foob", "MZXW6YQ=");
test("fooba", "MZXW6YTB");
test("r", "OI======");
test("foobar", "MZXW6YTBOI======");
/* This exits depending on whether all tests passed */
return exit_status();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment