From 91a37f88687e22e49333285d99542f799cbe6c56 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 7 Aug 2019 22:35:17 -0500 Subject: [PATCH] Add base64 code (unpadded standard alphabet) --- node/Utils.cpp | 91 ++++++++++++++++++++++++++++++++++++++++++++++---- node/Utils.hpp | 4 +++ selftest.cpp | 40 ++++++++++++++++++++++ 3 files changed, 129 insertions(+), 6 deletions(-) diff --git a/node/Utils.cpp b/node/Utils.cpp index 69a338be7..3c924518f 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -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 . + * along with this program. If not,see . * * -- * @@ -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= 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 diff --git a/node/Utils.hpp b/node/Utils.hpp index 1cb5ceb9c..f28158b01 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -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) * diff --git a/selftest.cpp b/selftest.cpp index 8ded0f310..e93127e8a 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -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);