Rusty Russell
7 years ago
7 changed files with 341 additions and 0 deletions
@ -0,0 +1 @@ |
|||
../../../licenses/CC0 |
@ -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; |
|||
} |
@ -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; |
|||
} |
@ -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 */ |
@ -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(); |
|||
} |
@ -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…
Reference in new issue