This commit is contained in:
iranl
2024-05-24 22:05:42 +02:00
parent 79febfd14e
commit 69679dfeca
23 changed files with 1522 additions and 232 deletions

View File

@@ -0,0 +1,21 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This cose is based on MacOSX code from taken from https://github.com/ekscrypto/Base32/
which itself is under the Unlicense Mhttps://unlicense.org>.
This code was based on code by Dave Poirier date 12-06-14 who released this
as "Public Domain"
The test vectors where taken from the https://www.rfc-editor.org/rfc/rfc4648
and are Copyright (C) The Internet Society (2006).

View File

@@ -0,0 +1,60 @@
# Arduino libary for Base32 (rfc4648) decoding.
## Traditional C interface
### base32decode : decode a \0 terminated base32 encoded string.
int base32decode(
const char * encoded,
unsigned char * decodedBytes,
size_t maxbuf
);
#### inputs:
encoded \0 terminated char buffer with encoded string
decodedBytes outputbuffer (or NULL)
maxbuff size of the output buffer
#### outputs:
returns the decoded byte array in decodedBytes and the length. Or if
decodedBytes==NULL, will just return the length needed; regardless of
the value of maxbuff.
If the size of maxbuff allows it - a terminating \0 is added (but not
including in the length returned) - as very often the decoded data
itself is actually again a string.
## C++/INO/Arduino# tring interface
### base32decodeToString - decode a String into a decoded String
int base32decodeToString(String encoded, String &decoded);
#### inputs:
encoded String with the encoded base32 value
&decoded returned string (if any)
#### outputs:
Will return the length of the decoded string or a negative
value on error.
# Example
Typical use:
String in = "IFZGI5LJNZXSAUTVNRSXU===";
String out;
int r = base32decodeToString(in, out);
if (r < 0) {
Serial.println("Could not decode the string");
return;
};
Serial.print("Decoded: ");
Serial.println(out);

View File

@@ -0,0 +1,127 @@
#include <limits.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <Base32-Decode.h>
void example1() {
String in, out;
in = "IFZGI5LJNZXSAUTVNRSXU===";
int r = base32decodeToString(in, out);
if (r < 0) {
Serial.println("Could not decode string");
return;
}
Serial.print("Decoded: ");
Serial.println(out);
}
void example2() {
const char * in = "IFZGI5LJNZXSAUTVNRSXU===";
size_t maxout = strlen(in); // we know that the encoded string is as long, or shorter than the decoded string.
char out[maxout];
int r = base32decode(in, (unsigned char*) out, maxout);
if (r < 0) {
Serial.println("Could not decode string");
return;
}
Serial.print("Decoded: ");
Serial.println(out);
}
void example3() {
const char * in = "IFZGI5LJNZXSAUTVNRSXU===";
// figure out the lenght we're going to get
//
int maxout = base32decode(in, NULL, 0);
// keep room for an terminating \0
maxout += 1;
// declare just enough memory
char out[maxout];
int r = base32decode(in, (unsigned char*) out, maxout);
if (r < 0) {
Serial.println("Could not decode string");
return;
}
Serial.print("Decoded: ");
Serial.println(out);
}
// RFC 4648 test vectors - https://www.rfc-editor.org/rfc/rfc4648 section 10
void runalltests() {
typedef struct testvector_t {
char *out;
char *in;
} testvector_t;
testvector_t testvectors[] = {
// normal with padding
{ (char *) "", (char *)""},
{ (char *) "f", (char *)"MY======"},
{ (char *) "fo", (char *)"MZXQ===="},
{ (char *) "foo", (char *)"MZXW6==="},
{ (char *) "foob", (char *)"MZXW6YQ="},
{ (char *) "fooba", (char *)"MZXW6YTB"},
{ (char *) "foobar", (char *)"MZXW6YTBOI======"},
// careless without the normal padding (but happens a lot)
{ (char *) "f", (char *)"MY"},
{ (char *) "fo", (char *) "MZXQ"},
{ (char *) "foo", (char *)"MZXW6"},
{ (char *) "foob", (char *)"MZXW6YQ"},
{ (char *) "fooba", (char *)"MZXW6YTB"},
{ (char *)"foobar", (char *)"MZXW6YTBOI"},
// wrong case.
{ (char *) "f", (char *)"my"},
{ (char *) "fo", (char *)"mzxq"},
{ (char *) "foo", (char *)"mzxw6"},
{ (char *) "foob", (char *)"mzxw6yq"},
{ (char *) "fooba", (char *)"mzxw6ytb"},
{ (char *)"foobar", (char *)"mzxw6ytboi"},
// acidental crufft (not in the RFC)
{ (char *)"", (char *)" "},
{ (char *)"", (char *)" "},
{ (char *)"foobar", (char *)" mzx w6 yt b o i"},
{ (char *)"foobar", (char *)" m zx w6 yt b o i"},
{ (char *)"foobar", (char *)"mzx\tw6ytboi"},
{ (char *)"foobar", (char *)"mzxw6\nytboi"},
{ (char *)"foobar", (char *)"mzxw6 ytb oi "}
};
for (int i = 0; i < sizeof(testvectors) / sizeof(testvector_t); i++) {
unsigned char buff[1024];
int ret = base32decode(testvectors[i].in, buff, sizeof(buff));
Serial.printf("test %d: %s -> '%s' == '%s' (size %d)\n", i + 1, testvectors[i].in, buff, testvectors[i].out, ret);
assert(ret == strlen(testvectors[i].out));
assert(strcmp((char *)buff, testvectors[i].out) == 0);
printf("test: %d ok\n\n", i + 1);
}
Serial.println("==\nAll test passed\n\n");
}
void setup() {
Serial.begin(119200);
while (!Serial) delay(10);
Serial.println("\n\n" __FILE__ " started");
// runalltests();
example1();
example2();
example3();
}
void loop() {
delay(10000);
}

View File

@@ -0,0 +1,12 @@
name=Base32-Decode
version=1.0.1
author=Dirk-Willem van Gulik et.al.
license=ASLv2, Unlicense, Public-Domain
maintainer=Dirk-Willem van Gulik <dirkx@webweaving.org>
sentence=Base32 decoder; able to handle both binary and string encoded data.
paragraph=RFC4648 Base32 decoder; handles both binary and string encoded data. With a char/unsigned-char interface as well as a String interface.
category=Communication
url=https://github.com/dirkx/Arduino-Base32-Decode
architectures=*
depends=
includes=Base32-Decode.h

View File

@@ -0,0 +1,174 @@
#include <Arduino.h>
#include <limits.h>
#include <string.h>
#include "Base32-Decode.h"
// Code and table taken from https://github.com/ekscrypto/Base32/
// under the Unlincense https://unlicense.org> and also by
// Dave Poirier on 12-06-14 who released this as "Public Domain"
//
int base32decode(const char * encoded, unsigned char * decoded, size_t maxbuf) {
#define __ 255
static char decodingTable[256] = {
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 0x00 - 0x0F
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 0x10 - 0x1F
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 0x20 - 0x2F
__, __, 26, 27, 28, 29, 30, 31, __, __, __, __, __, 0, __, __, // 0x30 - 0x3F
__, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 0x40 - 0x4F
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, __, __, __, __, __, // 0x50 - 0x5F
__, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 0x60 - 0x6F
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, __, __, __, __, __, // 0x70 - 0x7F
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 0x80 - 0x8F
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 0x90 - 0x9F
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 0xA0 - 0xAF
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 0xB0 - 0xBF
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 0xC0 - 0xCF
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 0xD0 - 0xDF
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 0xE0 - 0xEF
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 0xF0 - 0xFF
};
size_t encodedLength = strlen(encoded);
// strip any trailing padding.
while (encodedLength && encoded[encodedLength - 1] == '=') encodedLength--;
int blocks = (encodedLength + 7) >> 3;
int expectedDataLength = blocks * 5;
if (decoded == NULL)
return expectedDataLength + 1; // for terminating 0
if (maxbuf <= expectedDataLength)
return -1;
unsigned char encodedByte1, encodedByte2, encodedByte3, encodedByte4;
unsigned char encodedByte5, encodedByte6, encodedByte7, encodedByte8;
unsigned int encodedToProcess = encodedLength;
unsigned int encodedBaseIndex = 0;
unsigned int decodedBaseIndex = 0;
unsigned char block[8] = {0, 0, 0, 0, 0, 0, 0, 0};
unsigned int blockIndex = 0;
unsigned char c;
while ( encodedToProcess-- >= 1 ) {
c = encoded[encodedBaseIndex++];
if ( c == '=' ) break; // padding...
c = decodingTable[c];
if ( c == __ ) continue; // skip anything we do not know.
block[blockIndex++] = c;
if ( blockIndex == 8 ) {
encodedByte1 = block[0];
encodedByte2 = block[1];
encodedByte3 = block[2];
encodedByte4 = block[3];
encodedByte5 = block[4];
encodedByte6 = block[5];
encodedByte7 = block[6];
encodedByte8 = block[7];
decoded[decodedBaseIndex + 0] = ((encodedByte1 << 3) & 0xF8) | ((encodedByte2 >> 2) & 0x07);
decoded[decodedBaseIndex + 1] = ((encodedByte2 << 6) & 0xC0) | ((encodedByte3 << 1) & 0x3E) | ((encodedByte4 >> 4) & 0x01);
decoded[decodedBaseIndex + 2] = ((encodedByte4 << 4) & 0xF0) | ((encodedByte5 >> 1) & 0x0F);
decoded[decodedBaseIndex + 3] = ((encodedByte5 << 7) & 0x80) | ((encodedByte6 << 2) & 0x7C) | ((encodedByte7 >> 3) & 0x03);
decoded[decodedBaseIndex + 4] = ((encodedByte7 << 5) & 0xE0) | (encodedByte8 & 0x1F);
decodedBaseIndex += 5;
blockIndex = 0;
}
}
encodedByte7 = 0;
encodedByte6 = 0;
encodedByte5 = 0;
encodedByte4 = 0;
encodedByte3 = 0;
encodedByte2 = 0;
if (blockIndex > 6)
encodedByte7 = block[6];
if (blockIndex > 5)
encodedByte6 = block[5];
if (blockIndex > 4)
encodedByte5 = block[4];
if (blockIndex > 3)
encodedByte4 = block[3];
if (blockIndex > 2)
encodedByte3 = block[2];
if (blockIndex > 1)
encodedByte2 = block[1];
if (blockIndex > 0) {
encodedByte1 = block[0];
decoded[decodedBaseIndex + 0] = ((encodedByte1 << 3) & 0xF8) | ((encodedByte2 >> 2) & 0x07);
decoded[decodedBaseIndex + 1] = ((encodedByte2 << 6) & 0xC0) | ((encodedByte3 << 1) & 0x3E) | ((encodedByte4 >> 4) & 0x01);
decoded[decodedBaseIndex + 2] = ((encodedByte4 << 4) & 0xF0) | ((encodedByte5 >> 1) & 0x0F);
decoded[decodedBaseIndex + 3] = ((encodedByte5 << 7) & 0x80) | ((encodedByte6 << 2) & 0x7C) | ((encodedByte7 >> 3) & 0x03);
decoded[decodedBaseIndex + 4] = ((encodedByte7 << 5) & 0xE0);
};
static int paddingAdjustment[8] = {0, 1, 1, 1, 2, 3, 3, 4};
decodedBaseIndex += paddingAdjustment[blockIndex];
// ensure null terminated if there is space.
if (decodedBaseIndex < maxbuf)
decoded[decodedBaseIndex] = 0;
return decodedBaseIndex;
}
int base32decodeToString(String encoded, String &decoded) {
size_t maxlen = encoded.length() * 5 / 8 + 1;
char * buff = new char[maxlen];
int ret = base32decode(encoded.c_str(), (unsigned char*) buff, maxlen);
if (ret >= 0)
decoded = String(buff);
return ret;
}
#ifdef TEST_BASE32
#include <assert.h>
#include <stdio.h>
int main(int a, char **b) {
typedef struct testvector_t {
char *out;
char *in;
} testvector_t;
// RFC 4648 test vectors - https://www.rfc-editor.org/rfc/rfc4648 section 10
testvector_t testvectors[] = {
// normal with padding
{"", ""},
{"f", "MY======"},
{ "fo", "MZXQ===="},
{"foo", "MZXW6==="},
{"foob", "MZXW6YQ="},
{"fooba", "MZXW6YTB"},
{"foobar", "MZXW6YTBOI======"},
// careless without
{"f", "MY"},
{"fo", "MZXQ"},
{"foo", "MZXW6"},
{"foob", "MZXW6YQ"},
{"fooba", "MZXW6YTB"},
{ "foobar", "MZXW6YTBOI"},
// wrong case.
{"f", "my"},
{"fo", "mzxq"},
{"foo", "mzxw6"},
{"foob", "mzxw6yq"},
{"fooba", "mzxw6ytb"},
{ "foobar", "mzxw6ytboi"}
};
for (int i = 0; i < sizeof(testvectors) / sizeof(testvector_t); i++) {
char buff[1024];
int ret = base32decode(testvectors[i].in, buff, sizeof(buff));
printf("test %d: %s -> '%s' == '%s' (size %d)\n", i + 1, testvectors[i].in, buff, testvectors[i].out, ret);
assert(ret == strlen(testvectors[i].out));
assert(strcmp(buff, testvectors[i].out) == 0);
printf("test %d ok\n", i + 1);
}
return 0;
}
#endif

View File

@@ -0,0 +1,30 @@
#ifndef BASE32_DECODE_H
#define BASE32_DECODE_H
/* base32decode - decode a \0 terminated base32 encoded string.
*
* encoded \0 terminated char buffer with encoded string
* decodedBytes outputbuffer (or NULL)
* maxbuff size of the output buffer
*
* returns the decoded byte array in decodedBytes and the length. Or if
* decodedBytes==NULL, will just return the length needed; regardless of
* the value of maxbuff.
*
* If the size of maxbuff allows it - a terminating \0 is added (but not
* including in the length returned) - as very often the decoded data
* itself is actually again a string.
*/
extern int base32decode(const char * encoded, unsigned char * decodedBytes, size_t maxbuf);
/* base32decodeToString - decode a String into a decoded String
*
* encoded String with the encoded base32 value
* &decoded returned string (if any)
*
* Will return the length of the decoded string or a negative
* value on error.
*/
extern int base32decodeToString(String encoded, String &decoded);
#endif