1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2025-07-06 15:54:57 +03:00

Refactor Base64 functions, encode/decode using OpenSSL's EVP interface

Our own implementation works fine, however we should use OpenSSL's one since we already link to the library.

Base64Decode() and Base64Encode() return the required buffer size when "dst" is NULL.

This allows to efficiently allocate a buffer, without wasting memory or risking an overflow.

Base64FromBin() and Base64ToBin() perform all steps, returning a heap-allocated buffer with the data in it.
This commit is contained in:
Davide Beatrici
2021-07-02 09:24:41 +02:00
parent 03d67fd5b1
commit 233e28f38c
14 changed files with 224 additions and 369 deletions

View File

@ -57,9 +57,16 @@ if(UNIX)
# In some cases libiconv is not included in libc
find_library(LIB_ICONV iconv)
find_library(LIB_M m)
find_library(LIB_RT rt)
target_link_libraries(mayaqua PRIVATE Threads::Threads)
target_link_libraries(mayaqua
PRIVATE
Threads::Threads
$<$<BOOL:${LIB_ICONV}>:${LIB_ICONV}>
$<$<BOOL:${LIB_M}>:${LIB_M}>
$<$<BOOL:${LIB_RT}>:${LIB_RT}>
)
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(armv7l|aarch64|s390x)$" OR NOT HAVE_SYS_AUXV OR SKIP_CPU_FEATURES)
add_definitions(-DSKIP_CPU_FEATURES)
@ -69,14 +76,6 @@ if(UNIX)
target_link_libraries(mayaqua PRIVATE cpu_features)
endif()
if(LIB_RT)
target_link_libraries(mayaqua PRIVATE rt)
endif()
if(LIB_ICONV)
target_link_libraries(mayaqua PRIVATE ${LIB_ICONV})
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "SunOS")
target_link_libraries(mayaqua PRIVATE nsl socket)
endif()

View File

@ -7,6 +7,7 @@
#include "Cfg.h"
#include "Encoding.h"
#include "FileIO.h"
#include "Internat.h"
#include "Memory.h"
@ -746,12 +747,18 @@ bool CfgReadNextTextBUF(BUF *b, FOLDER *current)
if (!StrCmpi(token->Token[0], TAG_BYTE))
{
// byte
char *unescaped_b64 = CfgUnescape(data);
void *tmp = Malloc(StrLen(unescaped_b64) * 4 + 64);
int size = B64_Decode(tmp, unescaped_b64, StrLen(unescaped_b64));
CfgAddByte(current, name, tmp, size);
Free(tmp);
Free(unescaped_b64);
char *base64 = CfgUnescape(data);
const UINT base64_size = StrLen(base64);
UINT bin_size;
void *bin = Base64ToBin(&bin_size, base64, base64_size);
if (bin != NULL)
{
CfgAddByte(current, name, bin, bin_size);
Free(bin);
}
Free(base64);
}
Free(name);
@ -1162,9 +1169,7 @@ void CfgAddItemText(BUF *b, ITEM *t, UINT depth)
break;
case ITEM_TYPE_BYTE:
data = ZeroMalloc(t->size * 4 + 32);
len = B64_Encode(data, t->Buf, t->size);
data[len] = 0;
data = Base64FromBin(NULL, t->Buf, t->size);
break;
case ITEM_TYPE_STRING:

64
src/Mayaqua/Encoding.c Normal file
View File

@ -0,0 +1,64 @@
#include "Encoding.h"
#include <math.h>
#include <openssl/evp.h>
UINT Base64Decode(void *dst, const void *src, const UINT size)
{
if (dst == NULL)
{
// 4 input bytes = max. 3 output bytes.
//
// EVP_DecodeUpdate() ignores:
// - Leading/trailing whitespace.
// - Trailing newlines, carriage returns or EOF characters.
//
// EVP_DecodeFinal() fails if the input is not divisible by 4.
return size / 4 * 3;
}
// We don't use EVP_DecodeBlock() because it adds padding if the output is not divisible by 3.
EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new();
if (ctx == NULL)
{
return 0;
}
int ret = 0;
if (EVP_DecodeUpdate(ctx, dst, &ret, src, size) < 0)
{
goto FINAL;
}
int dummy;
if (EVP_DecodeFinal(ctx, dst, &dummy) < 0)
{
ret = 0;
}
FINAL:
EVP_ENCODE_CTX_free(ctx);
return ret;
}
UINT Base64Encode(void *dst, const void *src, const UINT size)
{
if (dst == NULL)
{
// 3 input bytes = 4 output bytes.
// +1 for the NUL terminator.
//
// EVP_EncodeBlock() adds padding when the input is not divisible by 3.
return ceilf((float)size / 3) * 4 + 1;
}
const int ret = EVP_EncodeBlock(dst, src, size);
if (ret > 0)
{
// EVP_EncodeBlock() returns the length of the string without the NUL terminator.
// We, instead, want to return the amount of bytes written into the output buffer.
return ret + 1;
}
return 0;
}

9
src/Mayaqua/Encoding.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef ENCODING_H
#define ENCODING_H
#include "MayaType.h"
UINT Base64Decode(void *dst, const void *src, const UINT size);
UINT Base64Encode(void *dst, const void *src, const UINT size);
#endif

View File

@ -7,6 +7,7 @@
#include "Memory.h"
#include "Encoding.h"
#include "Encrypt.h"
#include "FileIO.h"
#include "Internat.h"
@ -3407,232 +3408,62 @@ UINT64 Swap64(UINT64 value)
return r;
}
// Base64 encode
UINT Encode64(char *dst, char *src)
void *Base64ToBin(UINT *out_size, const void *src, const UINT size)
{
// Validate arguments
if (dst == NULL || src == NULL)
if (src == NULL || size == 0)
{
return 0;
return NULL;
}
return B64_Encode(dst, src, StrLen(src));
UINT bin_size = Base64Decode(NULL, src, size);
if (bin_size == 0)
{
return NULL;
}
void *bin = Malloc(bin_size);
bin_size = Base64Decode(bin, src, size);
if (bin_size == 0)
{
Free(bin);
return NULL;
}
if (out_size != NULL)
{
*out_size = bin_size;
}
return bin;
}
// Base64 decoding
UINT Decode64(char *dst, char *src)
void *Base64FromBin(UINT *out_size, const void *src, const UINT size)
{
// Validate arguments
if (dst == NULL || src == NULL)
if (src == NULL || size == 0)
{
return 0;
return NULL;
}
return B64_Decode(dst, src, StrLen(src));
}
UINT base64_size = Base64Encode(NULL, src, size);
if (base64_size == 0)
{
return NULL;
}
// Base64 encode
int B64_Encode(char *set, char *source, int len)
{
BYTE *src;
int i,j;
src = (BYTE *)source;
j = 0;
i = 0;
if (!len)
void *base64 = Malloc(base64_size);
base64_size = Base64Encode(base64, src, size);
if (base64_size == 0)
{
return 0;
Free(base64);
return NULL;
}
while (true)
{
if (i >= len)
{
return j;
}
if (set)
{
set[j] = B64_CodeToChar((src[i]) >> 2);
}
if (i + 1 >= len)
{
if (set)
{
set[j + 1] = B64_CodeToChar((src[i] & 0x03) << 4);
set[j + 2] = '=';
set[j + 3] = '=';
}
return j + 4;
}
if (set)
{
set[j + 1] = B64_CodeToChar(((src[i] & 0x03) << 4) + ((src[i + 1] >> 4)));
}
if (i + 2 >= len)
{
if (set)
{
set[j + 2] = B64_CodeToChar((src[i + 1] & 0x0f) << 2);
set[j + 3] = '=';
}
return j + 4;
}
if (set)
{
set[j + 2] = B64_CodeToChar(((src[i + 1] & 0x0f) << 2) + ((src[i + 2] >> 6)));
set[j + 3] = B64_CodeToChar(src[i + 2] & 0x3f);
}
i += 3;
j += 4;
}
}
// Base64 decode
int B64_Decode(char *set, char *source, int len)
{
int i,j;
char a1,a2,a3,a4;
char *src;
int f1,f2,f3,f4;
src = source;
i = 0;
j = 0;
while (true)
if (out_size != NULL)
{
f1 = f2 = f3 = f4 = 0;
if (i >= len)
{
break;
}
f1 = 1;
a1 = B64_CharToCode(src[i]);
if (a1 == -1)
{
f1 = 0;
}
if (i >= len + 1)
{
a2 = 0;
}
else
{
a2 = B64_CharToCode(src[i + 1]);
f2 = 1;
if (a2 == -1)
{
f2 = 0;
}
}
if (i >= len + 2)
{
a3 = 0;
}
else
{
a3 = B64_CharToCode(src[i + 2]);
f3 = 1;
if (a3 == -1)
{
f3 = 0;
}
}
if (i >= len + 3)
{
a4 = 0;
}
else
{
a4 = B64_CharToCode(src[i + 3]);
f4 = 1;
if (a4 == -1)
{
f4 = 0;
}
}
if (f1 && f2)
{
if (set)
{
set[j] = (a1 << 2) + (a2 >> 4);
}
j++;
}
if (f2 && f3)
{
if (set)
{
set[j] = (a2 << 4) + (a3 >> 2);
}
j++;
}
if (f3 && f4)
{
if (set)
{
set[j] = (a3 << 6) + a4;
}
j++;
}
i += 4;
*out_size = base64_size;
}
return j;
}
// Base64 : Convert a code to a character
char B64_CodeToChar(BYTE c)
{
BYTE r;
r = '=';
if (c <= 0x19)
{
r = c + 'A';
}
if (c >= 0x1a && c <= 0x33)
{
r = c - 0x1a + 'a';
}
if (c >= 0x34 && c <= 0x3d)
{
r = c - 0x34 + '0';
}
if (c == 0x3e)
{
r = '+';
}
if (c == 0x3f)
{
r = '/';
}
return r;
}
// Base64 : Convert a character to a code
char B64_CharToCode(char c)
{
if (c >= 'A' && c <= 'Z')
{
return c - 'A';
}
if (c >= 'a' && c <= 'z')
{
return c - 'a' + 0x1a;
}
if (c >= '0' && c <= '9')
{
return c - '0' + 0x34;
}
if (c == '+')
{
return 0x3e;
}
if (c == '/')
{
return 0x3f;
}
if (c == '=')
{
return -1;
}
return 0;
return base64;
}
// Malloc

View File

@ -190,12 +190,8 @@ void Zero(void *addr, UINT size);
void *Clone(void *addr, UINT size);
void *AddHead(void *src, UINT src_size, void *head, UINT head_size);
char B64_CodeToChar(BYTE c);
char B64_CharToCode(char c);
int B64_Encode(char *set, char *source, int len);
int B64_Decode(char *set, char *source, int len);
UINT Encode64(char *dst, char *src);
UINT Decode64(char *dst, char *src);
void *Base64FromBin(UINT *out_size, const void *src, const UINT size);
void *Base64ToBin(UINT *out_size, const void *src, const UINT size);
USHORT Swap16(USHORT value);
UINT Swap32(UINT value);

View File

@ -2239,10 +2239,9 @@ bool JsonTryParseValueAddToPack(PACK *p, JSON_VALUE *v, char *v_name, UINT index
{
if (v->type == JSON_TYPE_STRING)
{
UINT len = StrLen(v->value.string);
UCHAR *data = ZeroMalloc(len * 4 + 64);
UINT size = B64_Decode(data, v->value.string, len);
ElementNullSafe(PackAddDataEx(p, name, data, size, index, total))->JsonHint_IsArray = !is_single;
UINT data_size;
void *data = Base64ToBin(&data_size, v->value.string, StrLen(v->value.string));
ElementNullSafe(PackAddDataEx(p, name, data, data_size, index, total))->JsonHint_IsArray = !is_single;
Free(data);
ok = true;
}

View File

@ -129,17 +129,12 @@ UINT ProxyHttpConnect(PROXY_PARAM_OUT *out, PROXY_PARAM_IN *in, volatile bool *c
if (use_auth && GetHttpValue(h, "Proxy-Authorization") == NULL)
{
char auth_str[MAX_SIZE * 2], auth_b64_str[MAX_SIZE * 2];
// Generate the authentication string
char auth_str[MAX_SIZE * 2];
Format(auth_str, sizeof(auth_str), "%s:%s", in->Username, in->Password);
// Base64 encode
Zero(auth_b64_str, sizeof(auth_b64_str));
Encode64(auth_b64_str, auth_str);
// Generate final string
Format(auth_str, sizeof(auth_str), "Basic %s", auth_b64_str);
char *base64 = Base64FromBin(NULL, auth_str, StrLen(auth_str));
Format(auth_str, sizeof(auth_str), "Basic %s", base64);
Free(base64);
AddHttpValue(h, NewHttpValue("Proxy-Authorization", auth_str));
}

View File

@ -4667,12 +4667,11 @@ UINT JsonArrayAddNumber(JSON_ARRAY *array, UINT64 number) {
UINT JsonArrayAddData(JSON_ARRAY *array, void *data, UINT size)
{
UINT ret;
char *b64 = ZeroMalloc(size * 4 + 32);
B64_Encode(b64, data, size);
char *base64 = Base64FromBin(NULL, data, size);
ret = JsonArrayAddStr(array, b64);
ret = JsonArrayAddStr(array, base64);
Free(b64);
Free(base64);
return ret;
}
@ -4724,12 +4723,11 @@ UINT JsonSet(JSON_OBJECT *object, char *name, JSON_VALUE *value) {
UINT JsonSetData(JSON_OBJECT *object, char *name, void *data, UINT size)
{
UINT ret;
char *b64 = ZeroMalloc(size * 4 + 32);
B64_Encode(b64, data, size);
char *base64 = Base64FromBin(NULL, data, size);
ret = JsonSetStr(object, name, b64);
ret = JsonSetStr(object, name, base64);
Free(b64);
Free(base64);
return ret;
}