Browse Source

ccan: import base32 module.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 7 years ago
parent
commit
53c6ceeab9
  1. 4
      Makefile
  2. 1
      ccan/ccan/str/base32/LICENSE
  3. 26
      ccan/ccan/str/base32/_info
  4. 159
      ccan/ccan/str/base32/base32.c
  5. 77
      ccan/ccan/str/base32/base32.h
  6. 38
      ccan/ccan/str/base32/test/run-lower.c
  7. 36
      ccan/ccan/str/base32/test/run.c

4
Makefile

@ -79,6 +79,7 @@ CCAN_OBJS := \
ccan-ptr_valid.o \
ccan-rbuf.o \
ccan-read_write_all.o \
ccan-str-base32.o \
ccan-str-hex.o \
ccan-str.o \
ccan-take.o \
@ -138,6 +139,7 @@ CCAN_HEADERS := \
$(CCANDIR)/ccan/rbuf/rbuf.h \
$(CCANDIR)/ccan/read_write_all/read_write_all.h \
$(CCANDIR)/ccan/short_types/short_types.h \
$(CCANDIR)/ccan/str/base32/base32.h \
$(CCANDIR)/ccan/str/hex/hex.h \
$(CCANDIR)/ccan/str/str.h \
$(CCANDIR)/ccan/str/str_debug.h \
@ -549,3 +551,5 @@ ccan-bitops.o: $(CCANDIR)/ccan/bitops/bitops.c
$(CC) $(CFLAGS) -c -o $@ $<
ccan-rbuf.o: $(CCANDIR)/ccan/rbuf/rbuf.c
$(CC) $(CFLAGS) -c -o $@ $<
ccan-str-base32.o: $(CCANDIR)/ccan/str/base32/base32.c
$(CC) $(CFLAGS) -c -o $@ $<

1
ccan/ccan/str/base32/LICENSE

@ -0,0 +1 @@
../../../licenses/CC0

26
ccan/ccan/str/base32/_info

@ -0,0 +1,26 @@
#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;
}

159
ccan/ccan/str/base32/base32.c

@ -0,0 +1,159 @@
/* 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 */
const char *base32_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=";
/* 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] == base32_chars[32] && 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++) {
const char *p;
acc <<= 5;
p = memchr(base32_chars, c[i], 32);
if (!p) {
if (c[i] == base32_chars[32]) {
num_pad++;
continue;
}
return false;
}
/* Can't have padding then non-pad */
if (num_pad)
return false;
acc |= (p - base32_chars);
}
*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;
assert(bytes > 0 && bytes <= 5);
memcpy((char *)&val + 3, buf, bytes);
res = be64_to_cpu(val);
while (bits > 0) {
*dest = base32_chars[(res >> 35) & 0x1F];
dest++;
res <<= 5;
bits -= 5;
}
if (bytes != 5)
memset(dest, base32_chars[32], 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;
}

77
ccan/ccan/str/base32/base32.h

@ -0,0 +1,77 @@
/* 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);
/**
* base32_chars - the encoding/decoding array to use.
*
* It must be at least 33 characters long, representing 32 values and
* the pad value. The default array is "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=".
*/
extern const char *base32_chars;
#endif /* CCAN_STR_BASE32_H */

38
ccan/ccan/str/base32/test/run-lower.c

@ -0,0 +1,38 @@
#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);
base32_chars = "abcdefghijklmnopqrstuvwxyz234567=";
/* Test vectors from RFC, but lower-case */
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();
}

36
ccan/ccan/str/base32/test/run.c

@ -0,0 +1,36 @@
#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();
}
Loading…
Cancel
Save