Add base64 code (unpadded standard alphabet)

This commit is contained in:
Adam Ierymenko 2019-08-07 22:35:17 -05:00
parent 1a2ff884b3
commit 91a37f8868
No known key found for this signature in database
GPG Key ID: 1657198823E52A61
3 changed files with 129 additions and 6 deletions

View File

@ -1,10 +1,10 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/
* Copyright (C) 2011-2019 ZeroTier,Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation,either version 3 of the License,or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@ -13,7 +13,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* along with this program. If not,see <http://www.gnu.org/licenses/>.
*
* --
*
@ -69,7 +69,7 @@ static unsigned long _Utils_itoa(unsigned long n,char *s)
if (n == 0)
return 0;
unsigned long pos = _Utils_itoa(n / 10,s);
if (pos >= 22) // sanity check, should be impossible
if (pos >= 22) // sanity check,should be impossible
pos = 22;
s[pos] = '0' + (char)(n % 10);
return pos + 1;
@ -99,7 +99,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
* CSPRNG we use. There have been several bugs at the OS or OS distribution
* level in the past that resulted in systematically weak or predictable
* keys due to random seeding problems. This mitigates that by grabbing
* a bit of extra entropy and further randomizing the result, and comes
* a bit of extra entropy and further randomizing the result,and comes
* at almost no cost and with no real downside if the random source is
* good. */
if (!s20Initialized) {
@ -171,7 +171,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
#endif // __WINDOWS__ or not
}
int Utils::b32d(const char *encoded, uint8_t *result, int bufSize)
int Utils::b32d(const char *encoded,uint8_t *result,int bufSize)
{
int buffer = 0;
int bitsLeft = 0;
@ -247,4 +247,83 @@ int Utils::b32e(const uint8_t *data,int length,char *result,int bufSize)
return -1;
}
unsigned int Utils::b64e(const uint8_t *in,unsigned int inlen,char *out,unsigned int outlen)
{
static const char base64en[64] = { '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','+','/' };
unsigned int i = 0,j = 0;
uint8_t l = 0;
int s = 0;
for (;i<inlen;++i) {
uint8_t c = in[i];
switch (s) {
case 0:
s = 1;
if (j >= outlen) return 0;
out[j++] = base64en[(c >> 2) & 0x3f];
break;
case 1:
s = 2;
if (j >= outlen) return 0;
out[j++] = base64en[((l & 0x3) << 4) | ((c >> 4) & 0xf)];
break;
case 2:
s = 0;
if (j >= outlen) return 0;
out[j++] = base64en[((l & 0xf) << 2) | ((c >> 6) & 0x3)];
if (j >= outlen) return 0;
out[j++] = base64en[c & 0x3f];
break;
}
l = c;
}
switch (s) {
case 1:
if (j >= outlen) return 0;
out[j++] = base64en[(l & 0x3) << 4];
//out[j++] = '=';
//out[j++] = '=';
break;
case 2:
if (j >= outlen) return 0;
out[j++] = base64en[(l & 0xf) << 2];
//out[j++] = '=';
break;
}
if (j >= outlen) return 0;
out[j] = 0;
return j;
}
unsigned int Utils::b64d(const char *in,unsigned char *out,unsigned int outlen)
{
static const uint8_t base64de[256] = { 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,62,255,255,255,63,52,53,54,55,56,57,58,59,60,61,255,255,255,255,255,255,255,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,255,255,255,255,255,255,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,255,255,255,255,255 };
unsigned int i = 0;
unsigned int j = 0;
while ((in[i] != '=')&&(in[i] != 0)) {
if (j >= outlen)
break;
uint8_t c = base64de[(unsigned char)in[i]];
if (c != 255) {
switch (i & 0x3) {
case 0:
out[j] = (c << 2) & 0xff;
break;
case 1:
out[j++] |= (c >> 4) & 0x3;
out[j] = (c & 0xf) << 4;
break;
case 2:
out[j++] |= (c >> 2) & 0xf;
out[j] = (c & 0x3) << 6;
break;
case 3:
out[j++] |= c;
break;
}
}
++i;
}
return j;
}
} // namespace ZeroTier

View File

@ -245,6 +245,10 @@ public:
static int b32d(const char *encoded, uint8_t *result, int bufSize);
static int b32e(const uint8_t *data,int length,char *result,int bufSize);
static inline unsigned int b64MaxEncodedSize(const unsigned int s) { return ((((s + 2) / 3) * 4) + 1); }
static unsigned int b64e(const uint8_t *in,unsigned int inlen,char *out,unsigned int outlen);
static unsigned int b64d(const char *in,uint8_t *out,unsigned int outlen);
/**
* Tokenize a string (alias for strtok_r or strtok_s depending on platform)
*

View File

@ -712,6 +712,46 @@ static int testOther()
return -1;
}
std::cout << "[other] Testing base32... "; std::cout.flush();
for(unsigned int i=1;i<1024;++i) {
Utils::getSecureRandom(buf,(unsigned int)sizeof(buf));
int l = Utils::b32e((const uint8_t *)buf,i,buf2,sizeof(buf2));
if (l <= 0) {
std::cout << "FAIL (encode returned 0)" << std::endl;
return -1;
}
int l2 = Utils::b32d(buf2,(uint8_t *)buf3,sizeof(buf3));
if (l2 != (int)i) {
std::cout << "FAIL (decode returned wrong count)" << std::endl;
return -1;
}
if (memcmp(buf,buf3,i) != 0) {
std::cout << "FAIL (decode result incorrect)" << std::endl;
return -1;
}
}
std::cout << "PASS" << std::endl;
std::cout << "[other] Testing base64... "; std::cout.flush();
for(unsigned int i=1;i<1024;++i) {
Utils::getSecureRandom(buf,(unsigned int)sizeof(buf));
unsigned int l = Utils::b64e((const uint8_t *)buf,i,buf2,sizeof(buf2));
if (l == 0) {
std::cout << "FAIL (encode returned 0)" << std::endl;
return -1;
}
unsigned int l2 = Utils::b64d(buf2,(uint8_t *)buf3,sizeof(buf3));
if (l2 != i) {
std::cout << "FAIL (decode returned wrong count)" << std::endl;
return -1;
}
if (memcmp(buf,buf3,i) != 0) {
std::cout << "FAIL (decode result incorrect)" << std::endl;
return -1;
}
}
std::cout << "PASS" << std::endl;
std::cout << "[other] Testing InetAddress encode/decode..."; std::cout.flush();
std::cout << " " << InetAddress("127.0.0.1/9993").toString(buf);
std::cout << " " << InetAddress("feed:dead:babe:dead:beef:f00d:1234:5678/12345").toString(buf);