Commit a3959134 authored by Olaf Bergmann's avatar Olaf Bergmann

Base64 encoder/decoder wrapper

parent 4357629c
......@@ -37,6 +37,7 @@ libdcaf_a_SOURCES = \
src/dcaf.c \
src/dcaf_address.c \
src/dcaf_am.c \
src/dcaf_base64.c \
src/dcaf_cbor.c \
src/dcaf_coap.c \
src/dcaf_crypto_openssl.c \
......@@ -58,6 +59,7 @@ libdcaf_include_HEADERS = \
$(top_srcdir)/include/dcaf/cose_types.h \
$(top_srcdir)/include/dcaf/dcaf.h \
$(top_srcdir)/include/dcaf/dcaf_am.h \
$(top_srcdir)/include/dcaf/dcaf_base64.h \
$(top_srcdir)/include/dcaf/dcaf_cbor.h \
$(top_srcdir)/include/dcaf/dcaf_coap.h \
$(top_srcdir)/include/dcaf/dcaf_crypto.h \
......
/*
* dcaf_base64.h -- base64 wrapper for DCAF
*
* Copyright (C) 2020 Olaf Bergmann <bergmann@tzi.org>
*
* This file is part of the DCAF library libdcaf. Please see README
* for terms of use.
*/
#ifndef DCAF_BASE64_H
#define DCAF_BASE64_H 1
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/**
* Base64-encodes @p srclen bytes from @p src into the buffer @p
* dst. @p *dstlen specifies the size of @p dst and is overwritten
* with the number of bytes written. In case of error, the value of @p
* *dstlen is undefined.
*
* @param dst The destination buffer to hold the base64-encoded input.
* @param dstlen A pointer that is updated with the number of bytes
* that have actually been written. The initial value of
* of @p *dstlen must denote the maximum size of @p dst.
* @param src The source to be encoded.
* @param srclen The size of @p src in bytes.
*
* @return True if @p src successfully has been encoded, false otherwise.
* In case of an error, the value written to @p *dstlen is undefined.
*/
bool dcaf_base64_encode(uint8_t *dst, size_t *dstlen, const uint8_t *src, size_t srclen);
/**
* Base64-decodes @p srclen bytes from @p src into the buffer @p
* dst. @p *dstlen specifies the size of @p dst and is overwritten
* with the number of bytes written. In case of error, the value of @p
* *dstlen is undefined.
*
* @param dst The destination buffer to hold the decoded data.
* @param dstlen A pointer that is updated with the number of bytes
* that have actually been written. The initial value of
* of @p *dstlen must denote the maximum size of @p dst.
* @param src The source to be Base64-decoded.
* @param srclen The size of @p src in bytes.
*
* @return True if @p src successfully has been decoded, false otherwise.
* In case of an error, the value written to @p *dstlen is undefined.
*/
bool dcaf_base64_decode(uint8_t *dst, size_t *dstlen, const uint8_t *src, size_t srclen);
#endif /* DCAF_BASE64_H */
......@@ -20,6 +20,7 @@ extern "C" {
#include "aif.h"
#include "dcaf.h"
#include "dcaf_base64.h"
#include "dcaf_mem.h"
#include "dcaf_coap.h"
#include "state.h"
......
/*
* dcaf_base64.c -- base64 wrapper for DCAF
*
* Copyright (C) 2020 Olaf Bergmann <bergmann@tzi.org>
*
* This file is part of the DCAF library libdcaf. Please see README
* for terms of use.
*/
#include <assert.h>
#include <stdio.h>
#include "dcaf/dcaf_base64.h"
#ifdef COAP_DTLS_MBEDTLS
#include <mbedtls/base64.h>
bool dcaf_base64_encode(uint8_t *dst, size_t *dstlen,
const uint8_t *src, size_t srclen) {
size_t olen;
if (mbedtls_base64_encode((unsigned char *)dst, *dstlen, &olen,
(const unsigned char *)src, srclen) != 0) {
return false;
}
*dstlen = olen;
return true;
}
bool dcaf_base64_decode(uint8_t *dst, size_t *dstlen,
const uint8_t *src, size_t srclen) {
size_t olen;
if (mbedtls_base64_decode((unsigned char *)dst, *dstlen, &olen,
(const unsigned char *)src, srclen) != 0) {
return false;
}
*dstlen = olen;
return true;
}
#elif defined(RIOT_VERSION)
#include <base64.h>
bool dcaf_base64_encode(uint8_t *dst, size_t *dstlen,
const uint8_t *src, size_t srclen) {
return base64_encode(src, srclen (unsigned char *)dst, *dstlen)
== BASE64_SUCCESS;
}
bool dcaf_base64_decode(uint8_t *dst, size_t *dstlen,
const uint8_t *src, size_t srclen) {
return base64_decode(src, srclen (unsigned char *)dst, *dstlen)
== BASE64_SUCCESS;
}
#else /* !COAP_DTLS_MBEDTLS && !RIOT_VERSION */
static const char alphabet[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
};
static int find_code(uint8_t c) {
uint8_t n;
for (n = 0; n < sizeof(alphabet) && alphabet[n] != c; n++)
;
return n < sizeof(alphabet) ? n : -1;
}
static const char pad = '=';
bool dcaf_base64_encode(uint8_t *dst, size_t *dstlen,
const uint8_t *src, size_t srclen) {
unsigned int state = 0;
uint8_t *p = dst;
uint32_t buf = 0;
const uint8_t mask = 0x3f;
while(srclen-- && (p < (dst + *dstlen))) {
buf <<= 8;
buf += *src;
/* TODO: length check for dst buffer */
state = (state + 1) % 3;
if (state == 0) { // output accumulated bit group
*p++ = alphabet[(buf >> 18) & mask];
*p++ = alphabet[(buf >> 12) & mask];
*p++ = alphabet[(buf >> 6) & mask];
*p++ = alphabet[buf & mask];
buf = 0;
}
src++;
}
// output remaining characters, if any
switch (state) {
case 2: {
buf <<= 8;
*p++ = alphabet[(buf >> 18) & mask];
*p++ = alphabet[(buf >> 12) & mask];
*p++ = alphabet[(buf >> 6) & mask];
*p++ = pad;
break;
}
case 1: {
buf <<= 16;
*p++ = alphabet[(buf >> 18) & mask];
*p++ = alphabet[(buf >> 12) & mask];
*p++ = pad;
*p++ = pad;
break;
}
default:
;
}
*dstlen = p - dst;
return true;
}
bool dcaf_base64_decode(uint8_t *dst, size_t *dstlen,
const uint8_t *src, size_t srclen) {
unsigned int state = 0;
uint8_t buf = 0;
int code;
uint8_t bits;
uint8_t *p = dst;
while (srclen-- && (p < (dst + *dstlen))) {
if (*src == pad) { // special treatment for padding character
if (state > 1) {
*p = buf;
}
*dstlen = p - dst;
return true;
}
code = find_code(*src);
// ignore unknown characters
if (code == -1)
continue;
bits = code;
switch (state) {
case 0: // handle first byte
buf = bits << 2;
break;
case 1: // handle second byte
(*p++) = buf | (bits >> 4);
buf = bits << 4;
break;
case 2: // handle third byte
(*p++) = buf | (bits >> 2);
buf = bits << 6;
break;
case 3: // handle fourth byte
(*p++) = buf | (bits & 0x3f);
buf = 0;
break;
default:
assert(false); /* never reached */
}
/* advance state (modulo 4) and src */
state = (state + 1) % 4;
src++;
}
*dstlen = p - dst;
return true;
}
#endif /* !COAP_DTLS_MBEDTLS && !RIOT_VERSION */
# tests/Makefile.am
#
# Copyright (C) 2018 Olaf Bergmann <bergmann@tzi.org>
# Copyright (C) 2018-2020 Olaf Bergmann <bergmann@tzi.org>
#
# This file is part of dcaf. Please see README and COPYING for terms
# of use.
......@@ -22,6 +22,7 @@ testdriver_DEPENDENCIES = $(top_builddir)/libdcaf.a
testdriver_SOURCES = \
testdriver.cc \
test_aif.cc \
test_base64.cc \
test_crypto.cc \
test_cose.cc \
test_keys.cc \
......
/*
* test_base64.cc -- Base64 encoder/decoder
*
* Copyright (C) 2020 Olaf Bergmann <bergmann@tzi.org>
*
* This file is part of the DCAF library libdcaf. Please see README
* for terms of use.
*/
#include <iostream>
#include <memory>
#include <functional>
#include "dcaf/dcaf.h"
#include "dcaf/dcaf_int.h"
#include "test.hh"
#include "catch.hpp"
struct test_vector {
const char *src;
size_t srclen;
const char *result;
size_t resultlen;
};
static const test_vector testdata[] =
{
/* test vectors from RFC 4648 */
{ "", 0, "", 0 },
{ "f", 1, "Zg==", 4 },
{ "fo", 2, "Zm8=", 4 },
{ "foo", 3, "Zm9v", 4 },
{ "foob", 4, "Zm9vYg==", 8 },
{ "fooba", 5, "Zm9vYmE=", 8 },
{ "foobar", 6, "Zm9vYmFy", 8 }
};
#define MAX_TEST_BUF_SIZE 1024
template<unsigned int n> void do_encode(void) {
GIVEN("A base64 test vector #" + std::to_string(n + 1)) {
const struct test_vector &v = testdata[n];
uint8_t buf[MAX_TEST_BUF_SIZE];
size_t buflen = sizeof(buf);
bool result;
WHEN("The string encoded as base64") {
result = dcaf_base64_encode(buf, &buflen,
reinterpret_cast<const uint8_t *>(v.src), v.srclen);
REQUIRE(result == true);
THEN("the output buffer holds the encoded data") {
REQUIRE(buflen == v.resultlen);
REQUIRE(memcmp(buf, v.result, v.resultlen) == 0);
}
}
}
}
/* The following two template functions are used to generate a series
* of subsequently numbered test runs for the test vectors. Call with
* any n >= 0.
*/
template<unsigned int n> void generate_encode_test(void) {
generate_encode_test<n - 1>();
do_encode<n>();
}
template<> void generate_encode_test<0>(void) {
do_encode<0>();
}
SCENARIO( "Base64 encoding", "[base64]" ) {
generate_encode_test<sizeof(testdata)/sizeof(testdata[0]) - 1>();
}
template<unsigned int n> void do_decode(void) {
GIVEN("A base64 test vector #" + std::to_string(n + 1)) {
const struct test_vector &v = testdata[n];
uint8_t buf[MAX_TEST_BUF_SIZE];
size_t buflen = sizeof(buf);
bool result;
WHEN("The string decoded from base64") {
result = dcaf_base64_decode(buf, &buflen,
reinterpret_cast<const uint8_t *>(v.result), v.resultlen);
REQUIRE(result == true);
THEN("the output buffer holds the decoded data") {
REQUIRE(buflen == v.srclen);
REQUIRE(memcmp(buf, v.src, v.srclen) == 0);
}
}
}
}
/* The following two template functions are used to generate a series
* of subsequently numbered test runs for the test vectors. Call with
* any n >= 0.
*/
template<unsigned int n> void generate_decode_test(void) {
generate_decode_test<n - 1>();
do_decode<n>();
}
template<> void generate_decode_test<0>(void) {
do_decode<0>();
}
SCENARIO( "Base64 decoding", "[base64]" ) {
generate_decode_test<sizeof(testdata)/sizeof(testdata[0]) - 1>();
}
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