* Rename librz/util/ubase64.c into librz/util/base64.c for consistency #5216 * Add Doxygen documentation to function in librz/util/base85.c #5216 * Add unit-tests for librz/util/base85.c #5216 * Refactor base85 API and internal functions #5216 base85 was using a FILE as an input and it used to print its result to STDOUT, which was very bad for having unit-testing or to use it to extend rz-hash functionality, since the solution I really had was through using pipes which isn't ideal as much as the refactoring one. * Extend rz-hash with base85 #5126 * Fix formatting #5216 * Extract base36 decoding function from subprojects/rzwinkd/iob_net.c to librz/util/base36.c #5216 - Also, added Doxygen docs to rz_base36_decode function * Added rz_base36_encode_dyn #5216 - this function encode a u64 value to it's char[13+1] base36 counterpart - later, it will be helpful in expanding the funcitionality of rz-hash - added its Doxygen docs * Expand rz-hash with base36 encoding/decoding #5216 * Refactor rz_base36_decode to return a suitable error code like -1 #5216 - that should help testing it later for valid and invalid decodings - keeping the original behaviour as it was in subprojects/rzwinkd/iob_net.c * Add unit-tests for base36 encoding & decoding functions #5216 * Fix formatting #5216 * Fix base36.h header file guards #5216 * Add base32 encoding & decoding #5216 added their Doxygen Docs as well * Add base32 encoding and decoding unti-tests #5216 * Expand rz-hash with base32 encoding/decoding functionality #5216 * Fix formatting #5216 * Fix add base32 to codec_name_bytes #5216 * Fix base32 docs #5216 * Add base16 encoding/decoding functions #5216 - added their Doxygen docs as well. * Add base16 unit-tests #5216 * Fix test_base85 conversions warnings #5216 * Expand rz-hash with base16 encoding/decoding functionality #5216 * Fix formatting #5216 * Fix doxygen docs #5216 * Restore subprojects/rizin-shell-parser/parser.c to match origin/dev * Fix the order by baseXX * Fix base16 - invert the logic in calculate_src_length * Fix base16 - intialize variables in rz_base16_encode * Fix base16 - null terminate the output buffer of rz_base16_encode_dyn * Fix base16 - get rid of unnecessary else in rz_base16_decode * Fix base16 - use `len & 1` instead of `(len % 2) != 0` * Fix base32 - invert the logic in calculate_src_length * Fix base32 - compress two return statments by using `rz_return_val_if_fail(src && dest, 0);` * Fix base32 - null terminate the output of rz_base32_encode * Fix base32 - get rid of unnecessary else in rz_base32_decode add more parentheses to split addition from mult for better clarity * Fix base32 - intialize variables in rz_base32_encode_dyn * Fix base36 - intialize `tmp` variable in rz_base36_encode_dyn * Fix base36 - use RZ_LOG_ERROR instead of eprintf * Fix base36 - use RZ_NULLABLE for the API interface * Fix base85 - use RZ_OUT & RZ_NULLABLE for the API interface * Fix base85 - use RZ_LOG_ERROR instead of eprintf * Fix base85 - use size_t instead of int for decode_tuple_buf * Fix base85 - remove unused varaible, `out_len`, in rz_base85_encode_dyn * Fix base85 - correct decode buffer size calculation The previous rz_base85_dec_buflen() underestimated the worst‑case output size (3 bytes per 4 input chars), causing overflows when using ‘z’/’y’ abbreviations. Update it to allocate 4 bytes per input character plus one for the NUL terminator, eliminating heap-buffer-overflow errors. * Fix formatting * Fix base85-test - use `newlines` variable to make sure line breaks were inserted as expected * Fix base85-test - remove unnecessary includes * Fix rz-hash test - updated rz-hash -L expected result * Fix crypto_base36 - use RZ_LOG_ERROR instead of eprintf * Fix base16 - compress rz_return_val_if_fail statements and move before locals * Update base16 - use hex.c existing encoding/decoding logic - base16.c encoding/decoding functions are now wrappers for hex.c `rz_hex_bin2str` and `rz_hex_str2bin` functions - updated related base16 unit testing and documentation - updated the integraion with rz-hash binary (or crypto_base16) * Fix formatting * Add documentation to rz_hex_bin2str * Fix base85 - add missing `reutrn` docs to rz_base85_encode function * Fix base85 - use RZ_NONNULL for dest and src function instead of RZ_NULLABLE * Fix base36 - use RZ_NONNULL for dest and src function instead of RZ_NULLABLE * Fix base16 - use RZ_NONNULL for dest and src function instead of RZ_NULLABLE * Fix base32 - use RZ_NONNULL for dest and src function instead of RZ_NULLABLE * Fix base16 - add missig checks for invalid inputs in rz_base16_encode & rz_base16_encode_dyn * Fix base32 - add missing checks for bad encoding * Fix - add missing SPDX * Fix Formatting * Add rz_base36_encode & rz_base36_decode_dyn functions to base36 API
This commit is contained in:
parent
3efdc9a318
commit
38a530dbe3
27 changed files with 1828 additions and 128 deletions
|
|
@ -38,7 +38,11 @@ static const struct {
|
|||
RzCryptoSelector bit;
|
||||
} codec_name_bytes[] = {
|
||||
{ "all", UT64_MAX },
|
||||
{ "base16", RZ_CODEC_B16 },
|
||||
{ "base32", RZ_CODEC_B32 },
|
||||
{ "base36", RZ_CODEC_B36 },
|
||||
{ "base64", RZ_CODEC_B64 },
|
||||
{ "base85", RZ_CODEC_B85 },
|
||||
{ "base91", RZ_CODEC_B91 },
|
||||
{ "punycode", RZ_CODEC_PUNYCODE },
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
crypto_plugins_list = [
|
||||
'aes',
|
||||
'aes_cbc',
|
||||
'base16',
|
||||
'base32',
|
||||
'base36',
|
||||
'base64',
|
||||
'base85',
|
||||
'base91',
|
||||
'blowfish',
|
||||
'cps2',
|
||||
|
|
@ -29,7 +33,11 @@ rz_crypto_sources = [
|
|||
'crypto.c',
|
||||
'p/crypto_aes.c',
|
||||
'p/crypto_aes_cbc.c',
|
||||
'p/crypto_base16.c',
|
||||
'p/crypto_base32.c',
|
||||
'p/crypto_base36.c',
|
||||
'p/crypto_base64.c',
|
||||
'p/crypto_base85.c',
|
||||
'p/crypto_base91.c',
|
||||
'p/crypto_blowfish.c',
|
||||
'p/crypto_cps2.c',
|
||||
|
|
|
|||
84
librz/crypto/p/crypto_base16.c
Normal file
84
librz/crypto/p/crypto_base16.c
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
// SPDX-FileCopyrightText: 2025 Ahmed Ibrahim <a.ibrahim8686@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_lib.h>
|
||||
#include <rz_crypto.h>
|
||||
#include <rz_util.h>
|
||||
|
||||
static bool base16_set_key(RzCrypto *cry, const ut8 *key, int keylen, int mode, int direction) {
|
||||
cry->dir = direction;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int base16_get_key_size(RzCrypto *cry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool base16_use(const char *algo) {
|
||||
return !strcmp(algo, "base16");
|
||||
}
|
||||
|
||||
static bool update(RzCrypto *cry, const ut8 *buf, int len) {
|
||||
if (!cry || !buf || len <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cry->dir == RZ_CRYPTO_DIR_ENCRYPT) {
|
||||
size_t outlen = 1 + 2 * len;
|
||||
char *encoded = malloc(outlen);
|
||||
if (!encoded) {
|
||||
return false;
|
||||
}
|
||||
rz_base16_encode(encoded, buf, len);
|
||||
rz_crypto_append(cry, (const ut8 *)encoded, strlen(encoded));
|
||||
free(encoded);
|
||||
|
||||
} else {
|
||||
size_t outlen = (size_t)(len / 2 + 1);
|
||||
ut8 *decoded = malloc(outlen);
|
||||
if (!decoded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
st64 decoded_len = rz_base16_decode(decoded, (const char *)buf);
|
||||
if (decoded_len == 0) { // truly invalid
|
||||
free(decoded);
|
||||
return false;
|
||||
}
|
||||
if (decoded_len < 0) { // odd nibbles handled, still valid
|
||||
decoded_len = -decoded_len;
|
||||
}
|
||||
|
||||
rz_crypto_append(cry, decoded, (size_t)decoded_len);
|
||||
free(decoded);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool final(RzCrypto *cry, const ut8 *buf, int len) {
|
||||
if (!buf) {
|
||||
return true;
|
||||
}
|
||||
return update(cry, buf, len);
|
||||
}
|
||||
|
||||
RzCryptoPlugin rz_crypto_plugin_base16 = {
|
||||
.name = "base16",
|
||||
.author = "Ahmed Ibrahim",
|
||||
.license = "LGPL-3",
|
||||
.description = "Base16 encoder/decoder",
|
||||
.set_key = base16_set_key,
|
||||
.get_key_size = base16_get_key_size,
|
||||
.use = base16_use,
|
||||
.update = update,
|
||||
.final = final
|
||||
};
|
||||
|
||||
#ifndef RZ_PLUGIN_INCORE
|
||||
RZ_API RzLibStruct rizin_plugin = {
|
||||
.type = RZ_LIB_TYPE_CRYPTO,
|
||||
.data = &rz_crypto_plugin_base16,
|
||||
.version = RZ_VERSION
|
||||
};
|
||||
#endif
|
||||
79
librz/crypto/p/crypto_base32.c
Normal file
79
librz/crypto/p/crypto_base32.c
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// SPDX-FileCopyrightText: 2025 Ahmed Ibrahim <a.ibrahim8686@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_lib.h>
|
||||
#include <rz_crypto.h>
|
||||
#include <rz_util.h>
|
||||
|
||||
static bool base32_set_key(RzCrypto *cry, const ut8 *key, int keylen, int mode, int direction) {
|
||||
cry->dir = direction;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int base32_get_key_size(RzCrypto *cry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool base32_use(const char *algo) {
|
||||
return !strcmp(algo, "base32");
|
||||
}
|
||||
|
||||
static bool update(RzCrypto *cry, const ut8 *buf, int len) {
|
||||
if (!cry || !buf || len <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cry->dir == RZ_CRYPTO_DIR_ENCRYPT) {
|
||||
size_t outlen = 1 + 8 * ((len + 4) / 5);
|
||||
char *encoded = malloc(outlen);
|
||||
if (!encoded) {
|
||||
return false;
|
||||
}
|
||||
rz_base32_encode(encoded, buf, len);
|
||||
rz_crypto_append(cry, (const ut8 *)encoded, strlen(encoded));
|
||||
free(encoded);
|
||||
|
||||
} else {
|
||||
size_t outlen = 1 + 5 * ((len + 7) / 8);
|
||||
ut8 *decoded = malloc(outlen);
|
||||
if (!decoded) {
|
||||
return false;
|
||||
}
|
||||
st64 decoded_len = rz_base32_decode(decoded, (const char *)buf, len);
|
||||
if (decoded_len == -1) {
|
||||
free(decoded);
|
||||
return false;
|
||||
}
|
||||
rz_crypto_append(cry, decoded, decoded_len);
|
||||
free(decoded);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool final(RzCrypto *cry, const ut8 *buf, int len) {
|
||||
if (!buf) {
|
||||
return true;
|
||||
}
|
||||
return update(cry, buf, len);
|
||||
}
|
||||
|
||||
RzCryptoPlugin rz_crypto_plugin_base32 = {
|
||||
.name = "base32",
|
||||
.author = "Ahmed Ibrahim",
|
||||
.license = "LGPL-3",
|
||||
.description = "Base32 encoder/decoder",
|
||||
.set_key = base32_set_key,
|
||||
.get_key_size = base32_get_key_size,
|
||||
.use = base32_use,
|
||||
.update = update,
|
||||
.final = final
|
||||
};
|
||||
|
||||
#ifndef RZ_PLUGIN_INCORE
|
||||
RZ_API RzLibStruct rizin_plugin = {
|
||||
.type = RZ_LIB_TYPE_CRYPTO,
|
||||
.data = &rz_crypto_plugin_base32,
|
||||
.version = RZ_VERSION
|
||||
};
|
||||
#endif
|
||||
105
librz/crypto/p/crypto_base36.c
Normal file
105
librz/crypto/p/crypto_base36.c
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// SPDX-FileCopyrightText: 2025 Ahmed Ibrahim <a.ibrahim8686@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_lib.h>
|
||||
#include <rz_crypto.h>
|
||||
#include <rz_util.h>
|
||||
|
||||
static bool base36_set_key(RzCrypto *cry, const ut8 *key, int keylen, int mode, int direction) {
|
||||
cry->dir = direction;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int base36_get_key_size(RzCrypto *cry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool base36_use(const char *algo) {
|
||||
return !strcmp(algo, "base36");
|
||||
}
|
||||
|
||||
static bool update(RzCrypto *cry, const ut8 *buf, int len) {
|
||||
if (!cry || !buf || len <= 0) {
|
||||
return false;
|
||||
}
|
||||
ut8 *out = NULL;
|
||||
size_t out_len = 0;
|
||||
|
||||
if (cry->dir == RZ_CRYPTO_DIR_ENCRYPT) {
|
||||
if (len > 8) { /* > 64‑bit won’t fit in our encoder */
|
||||
RZ_LOG_ERROR("base36: encoder supports up to 64‑bit values (%d > 8 bytes)\n", len);
|
||||
return false;
|
||||
}
|
||||
ut64 val = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
val = (val << 8) | buf[i];
|
||||
}
|
||||
char *enc = rz_base36_encode_dyn(val);
|
||||
if (!enc) {
|
||||
return false;
|
||||
}
|
||||
out = (ut8 *)enc;
|
||||
out_len = strlen(enc);
|
||||
|
||||
} else if (cry->dir == RZ_CRYPTO_DIR_DECRYPT) {
|
||||
ut64 val = 0;
|
||||
st64 ret = rz_base36_decode(&val, (const char *)buf, (size_t)len);
|
||||
/* Here, rz_base36_decode returns 0 either for the value 0 or on error.
|
||||
* We consider empty input an error. */
|
||||
if (ret < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// convert val → big‑endian byte array
|
||||
ut8 tmp[8];
|
||||
int idx = 8;
|
||||
if (val == 0) {
|
||||
tmp[--idx] = 0;
|
||||
} else {
|
||||
while (val > 0) {
|
||||
tmp[--idx] = (ut8)(val & 0xff);
|
||||
val >>= 8;
|
||||
}
|
||||
}
|
||||
out_len = 8 - idx;
|
||||
out = (ut8 *)malloc(out_len);
|
||||
if (!out) {
|
||||
return false;
|
||||
}
|
||||
memcpy(out, tmp + idx, out_len);
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
rz_crypto_append(cry, out, (int)out_len);
|
||||
free(out);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool final(RzCrypto *cry, const ut8 *buf, int len) {
|
||||
if (!buf || len == 0) {
|
||||
return true;
|
||||
}
|
||||
return update(cry, buf, len);
|
||||
}
|
||||
|
||||
RzCryptoPlugin rz_crypto_plugin_base36 = {
|
||||
.name = "base36",
|
||||
.author = "abcSup",
|
||||
.license = "LGPL-3",
|
||||
.description = "Base36 encoder/decoder",
|
||||
.set_key = base36_set_key,
|
||||
.get_key_size = base36_get_key_size,
|
||||
.use = base36_use,
|
||||
.update = update,
|
||||
.final = final
|
||||
};
|
||||
|
||||
#ifndef RZ_PLUGIN_INCORE
|
||||
RZ_API RzLibStruct rizin_plugin = {
|
||||
.type = RZ_LIB_TYPE_CRYPTO,
|
||||
.data = &rz_crypto_plugin_base36,
|
||||
.version = RZ_VERSION
|
||||
};
|
||||
#endif
|
||||
72
librz/crypto/p/crypto_base85.c
Normal file
72
librz/crypto/p/crypto_base85.c
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// SPDX-FileCopyrightText: 2025 Ahmed Ibrahim <a.ibrahim8686@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_lib.h>
|
||||
#include <rz_crypto.h>
|
||||
#include <rz_util.h>
|
||||
|
||||
static bool base85_set_key(RzCrypto *cry, const ut8 *key, int keylen, int mode, int direction) {
|
||||
cry->dir = direction;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int base85_get_key_size(RzCrypto *cry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool base85_use(const char *algo) {
|
||||
return !strcmp(algo, "base85");
|
||||
}
|
||||
|
||||
static bool update(RzCrypto *cry, const ut8 *buf, int len) {
|
||||
if (!cry || !buf || len <= 0) {
|
||||
return false;
|
||||
}
|
||||
ut8 *out = NULL;
|
||||
size_t out_len = 0;
|
||||
if (cry->dir == RZ_CRYPTO_DIR_ENCRYPT) {
|
||||
char *enc = rz_base85_encode_dyn((char *)buf, (size_t)len, 0, 0, 1);
|
||||
if (!enc) {
|
||||
return false;
|
||||
}
|
||||
out = (ut8 *)enc;
|
||||
out_len = strlen(enc);
|
||||
} else if (cry->dir == RZ_CRYPTO_DIR_DECRYPT) {
|
||||
out = (ut8 *)rz_base85_decode_dyn((const char *)buf, (st64)len, 0, 0, &out_len);
|
||||
if (!out) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
rz_crypto_append(cry, out, (int)out_len);
|
||||
free(out);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool final(RzCrypto *cry, const ut8 *buf, int len) {
|
||||
if (!buf || len == 0) {
|
||||
return true;
|
||||
}
|
||||
return update(cry, buf, len);
|
||||
}
|
||||
|
||||
RzCryptoPlugin rz_crypto_plugin_base85 = {
|
||||
.name = "base85",
|
||||
.author = "Ahmed Ibrahim",
|
||||
.license = "LGPL-3",
|
||||
.description = "Base85 encoder/decoder",
|
||||
.set_key = base85_set_key,
|
||||
.get_key_size = base85_get_key_size,
|
||||
.use = base85_use,
|
||||
.update = update,
|
||||
.final = final
|
||||
};
|
||||
|
||||
#ifndef RZ_PLUGIN_INCORE
|
||||
RZ_API RzLibStruct rizin_plugin = {
|
||||
.type = RZ_LIB_TYPE_CRYPTO,
|
||||
.data = &rz_crypto_plugin_base85,
|
||||
.version = RZ_VERSION
|
||||
};
|
||||
#endif
|
||||
|
|
@ -65,7 +65,11 @@ rz_util_files = [
|
|||
'rz_util/rz_asn1.h',
|
||||
'rz_util/rz_assert.h',
|
||||
'rz_util/rz_axml.h',
|
||||
'rz_util/rz_base16.h',
|
||||
'rz_util/rz_base32.h',
|
||||
'rz_util/rz_base36.h',
|
||||
'rz_util/rz_base64.h',
|
||||
'rz_util/rz_base85.h',
|
||||
'rz_util/rz_base91.h',
|
||||
'rz_util/rz_big.h',
|
||||
'rz_util/rz_bits.h',
|
||||
|
|
|
|||
|
|
@ -108,6 +108,10 @@ RZ_API RZ_BORROW const RzCryptoPlugin *rz_crypto_plugin_by_index(RZ_NONNULL RzCr
|
|||
#define RZ_CODEC_B64 1ULL
|
||||
#define RZ_CODEC_B91 1ULL << 1
|
||||
#define RZ_CODEC_PUNYCODE 1ULL << 2
|
||||
#define RZ_CODEC_B85 1ULL << 3
|
||||
#define RZ_CODEC_B36 1ULL << 4
|
||||
#define RZ_CODEC_B32 1ULL << 5
|
||||
#define RZ_CODEC_B16 1ULL << 6
|
||||
#define RZ_CODEC_ALL 0xFFFF
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
|
|
@ -29,7 +29,11 @@
|
|||
#include "rz_util/rz_rbtree.h"
|
||||
#include "rz_util/rz_intervaltree.h"
|
||||
#include "rz_util/rz_big.h"
|
||||
#include "rz_util/rz_base16.h"
|
||||
#include "rz_util/rz_base32.h"
|
||||
#include "rz_util/rz_base36.h"
|
||||
#include "rz_util/rz_base64.h"
|
||||
#include "rz_util/rz_base85.h"
|
||||
#include "rz_util/rz_base91.h"
|
||||
#include "rz_util/rz_buf.h"
|
||||
#include "rz_util/rz_bits.h"
|
||||
|
|
|
|||
23
librz/include/rz_util/rz_base16.h
Normal file
23
librz/include/rz_util/rz_base16.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// SPDX-FileCopyrightText: 2025 Ahmed Ibrahim <a.ibrahim8686@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef RZ_BASE16_H
|
||||
#define RZ_BASE16_H
|
||||
|
||||
#include <rz_types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
RZ_API int rz_base16_encode(RZ_OUT RZ_NONNULL char *bout, RZ_NONNULL const ut8 *bin, size_t sz);
|
||||
RZ_API int rz_base16_decode(RZ_OUT RZ_NONNULL ut8 *bout, RZ_NONNULL const char *bin);
|
||||
|
||||
RZ_API RZ_OWN char *rz_base16_encode_dyn(RZ_NONNULL const ut8 *bin, size_t sz);
|
||||
RZ_API RZ_OWN ut8 *rz_base16_decode_dyn(RZ_NONNULL const char *in, st64 len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // RZ_BASE16_H
|
||||
23
librz/include/rz_util/rz_base32.h
Normal file
23
librz/include/rz_util/rz_base32.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// SPDX-FileCopyrightText: 2025 Ahmed Ibrahim <a.ibrahim8686@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef RZ_BASE32_H
|
||||
#define RZ_BASE32_H
|
||||
|
||||
#include <rz_types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
RZ_API size_t rz_base32_encode(RZ_OUT RZ_NONNULL char *bout, RZ_NONNULL const ut8 *bin, size_t sz);
|
||||
RZ_API st64 rz_base32_decode(RZ_OUT RZ_NONNULL ut8 *bout, RZ_NONNULL const char *bin, st64 len);
|
||||
|
||||
RZ_API RZ_OWN char *rz_base32_encode_dyn(RZ_NONNULL const ut8 *bin, size_t sz);
|
||||
RZ_API RZ_OWN ut8 *rz_base32_decode_dyn(RZ_NONNULL const char *in, st64 len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // RZ_BASE32_H
|
||||
23
librz/include/rz_util/rz_base36.h
Normal file
23
librz/include/rz_util/rz_base36.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// SPDX-FileCopyrightText: 2025 Ahmed Ibrahim <a.ibrahim8686@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef RZ_BASE36_H
|
||||
#define RZ_BASE36_H
|
||||
|
||||
#include <rz_types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
RZ_API size_t rz_base36_encode(RZ_OUT RZ_NONNULL char *bout, ut64 val);
|
||||
RZ_API RZ_OWN char *rz_base36_encode_dyn(ut64 val);
|
||||
|
||||
RZ_API st64 rz_base36_decode(RZ_OUT RZ_NONNULL ut64 *bout, RZ_NONNULL const char *bin, st64 len);
|
||||
RZ_API RZ_OWN ut64 *rz_base36_decode_dyn(RZ_NONNULL const char *bin, const size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // RZ_BASE36_H
|
||||
23
librz/include/rz_util/rz_base85.h
Normal file
23
librz/include/rz_util/rz_base85.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// SPDX-FileCopyrightText: 2025 Ahmed Ibrahim <a.ibrahim8686@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef RZ_BASE85_H
|
||||
#define RZ_BASE85_H
|
||||
|
||||
#include <rz_types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
RZ_API int rz_base85_encode(RZ_OUT RZ_NONNULL char *dest, RZ_NONNULL const char *src, size_t n, int delims, int wrap, int y_abbr);
|
||||
RZ_API st64 rz_base85_decode(RZ_OUT RZ_NONNULL char *dest, RZ_NONNULL const char *src, st64 len, int delims, int ignore_garbage);
|
||||
|
||||
RZ_API RZ_OWN char *rz_base85_encode_dyn(RZ_NONNULL const char *src, size_t n, int delims, int wrap, int y_abbr);
|
||||
RZ_API RZ_OWN char *rz_base85_decode_dyn(RZ_NONNULL const char *src, st64 len, int delims, int ignore_garbage, size_t *out_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // RZ_BASE85_H
|
||||
147
librz/util/base16.c
Normal file
147
librz/util/base16.c
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
// SPDX-FileCopyrightText: 2025 Ahmed Ibrahim <a.ibrahim8686@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief Base16 encoding and decoding functions
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <rz_types_base.h>
|
||||
#include <rz_util.h>
|
||||
|
||||
/** \internal
|
||||
* \brief Calculate the length of \p src.
|
||||
* \param[in] src The binary data to be Base16-encoded later.
|
||||
* \param len The length of the binary data in bytes.
|
||||
*
|
||||
* This function returns \p len as it is unless it is negative, in
|
||||
* which case, it returns the string length of \p src. A possibility
|
||||
* of a string size larger than \c ST64_MAX requires us to make a
|
||||
* bounds check, and error if overflow is possible. This function is
|
||||
* provided in lieu of modifying the decoding API parameter list.
|
||||
*/
|
||||
static st64 calculate_src_length(const char *src, st64 len) {
|
||||
if (len >= 0)
|
||||
return len;
|
||||
size_t real_len = strlen(src);
|
||||
if (ST64_MAX < real_len) {
|
||||
return -1;
|
||||
}
|
||||
return (st64)real_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Base-16-encode binary data.
|
||||
* \param[out] dest Buffer to receive NUL-terminated encoded output.
|
||||
* Must have at least \c (2 × n) + 1 bytes.
|
||||
* \param[in] src Pointer to the binary input.
|
||||
* \param[in] n Number of bytes in \p src.
|
||||
* \return Number of characters written (excluding the NUL terminator),
|
||||
* or \c 0 if parameters are invalid.
|
||||
*
|
||||
* This function converts each input byte to two lowercase hexadecimal
|
||||
* characters using \c rz_hex_bin2str().
|
||||
*/
|
||||
RZ_API int rz_base16_encode(RZ_OUT RZ_NONNULL char *dest, RZ_NONNULL const ut8 *src, size_t n) {
|
||||
rz_return_val_if_fail(src && dest, 0);
|
||||
if (rz_hex_bin2str(src, (int)n, dest) == 0) {
|
||||
return 0;
|
||||
}
|
||||
return n * 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Dynamically allocate and fill a Base-16-encoded string.
|
||||
* \param[in] src Pointer to the binary input.
|
||||
* \param[in] n Number of bytes in \p src.
|
||||
* \return Pointer to a NUL-terminated Base-16 string allocated with
|
||||
* \c malloc, or \c NULL if allocation fails or \p src is \c NULL.
|
||||
* Caller must \c free() the returned pointer.
|
||||
*
|
||||
* Allocates exactly \c (2 × n) + 1 bytes, encodes the input, and NUL-terminates.
|
||||
*/
|
||||
RZ_API RZ_OWN char *rz_base16_encode_dyn(RZ_NONNULL const ut8 *src, size_t n) {
|
||||
rz_return_val_if_fail(src, NULL);
|
||||
char *out = (char *)malloc(2 * n + 1);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
if (rz_base16_encode(out, src, n) == 0) {
|
||||
return NULL;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Decode a Base-16 string into binary form.
|
||||
* \param[out] dest Output buffer for decoded bytes.
|
||||
* Must have at least (\c strlen(src) / 2) + 1 bytes.
|
||||
* \param[in] src NUL-terminated Base-16 string.
|
||||
* \return Number of decoded bytes on success,
|
||||
* or a negative value if an odd number of nibbles was parsed,
|
||||
* or \c 0 if parameters are invalid.
|
||||
*
|
||||
* This is a thin wrapper around \c rz_hex_str2bin().
|
||||
* Output is **not** automatically NUL-terminated unless you add it yourself.
|
||||
*/
|
||||
RZ_API int rz_base16_decode(RZ_OUT RZ_NONNULL ut8 *dest, RZ_NONNULL const char *src) {
|
||||
rz_return_val_if_fail(src && dest, 0);
|
||||
|
||||
int out_len = rz_hex_str2bin(src, dest);
|
||||
if (out_len < 0) {
|
||||
// Odd number of nibbles — still terminate after absolute length
|
||||
dest[-out_len] = '\0';
|
||||
return out_len;
|
||||
}
|
||||
|
||||
// NUL terminate after the decoded bytes
|
||||
dest[out_len] = '\0';
|
||||
return out_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Dynamically decode a Base-16 string into binary form.
|
||||
* \param[in] src NUL-terminated Base-16 string to decode.
|
||||
* \param[in] len Length of \p src in characters, or \c -1 to use \c strlen().
|
||||
* \return Pointer to a newly allocated buffer containing the decoded
|
||||
* binary data followed by a NUL byte, or \c NULL on error.
|
||||
*
|
||||
* Accepts even or odd numbers of hex digits. For odd lengths, the final
|
||||
* nibble is padded with zero.
|
||||
*/
|
||||
RZ_API RZ_OWN ut8 *rz_base16_decode_dyn(RZ_NONNULL const char *src, st64 len) {
|
||||
rz_return_val_if_fail(src, NULL);
|
||||
|
||||
len = calculate_src_length(src, len);
|
||||
if (len < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
st64 cap = (len / 2) + 1;
|
||||
ut8 *buf = (ut8 *)malloc((size_t)cap);
|
||||
if (!buf) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
st64 out_len = rz_base16_decode(buf, src);
|
||||
if (out_len == 0) { // truly invalid hex
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
if (out_len < 0) { // odd nibble count — still valid, pad added
|
||||
out_len = -out_len;
|
||||
}
|
||||
|
||||
buf[out_len] = '\0';
|
||||
if (out_len + 1 < cap) {
|
||||
ut8 *tmp = (ut8 *)realloc(buf, (size_t)out_len + 1);
|
||||
if (tmp) {
|
||||
buf = tmp;
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
354
librz/util/base32.c
Normal file
354
librz/util/base32.c
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
// SPDX-FileCopyrightText: 2025 Ahmed Ibrahim <a.ibrahim8686@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief Base32 encoding and decoding functions
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <rz_types_base.h>
|
||||
#include <rz_util.h>
|
||||
|
||||
/** \internal
|
||||
* \brief Table for mapping a 5‑bit value (0 – 31) to its Base‑32 symbol.
|
||||
*/
|
||||
static const char cb32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
|
||||
/** \internal
|
||||
* \brief Map a Base‑32 symbol to its 5‑bit index (0 – 31).
|
||||
*/
|
||||
static size_t cd32(int c) {
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
return (size_t)(c - 'A');
|
||||
}
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
return (size_t)(c - 'a');
|
||||
}
|
||||
if (c >= '2' && c <= '7') {
|
||||
return (size_t)(26 + (c - '2'));
|
||||
}
|
||||
return SIZE_MAX;
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief Base‑32 encode one 40‑bit (5‑byte) block.
|
||||
* \param[in] src Five input bytes.
|
||||
* \param[out] dest Eight‑character output (no padding chars).
|
||||
*
|
||||
* The caller must pad any partial final block with zeros *before*
|
||||
* invoking this function; the encoder later converts the surplus
|
||||
* characters to ‘=’ if RFC‑4648 padding is desired.
|
||||
*/
|
||||
static void pack_to5(ut8 dest[8], const ut8 src[5]) {
|
||||
/* Each line extracts a 5‑bit group from the 40‑bit stream */
|
||||
dest[0] = cb32[(src[0] >> 3) & 0x1F];
|
||||
dest[1] = cb32[((src[0] << 2) | (src[1] >> 6)) & 0x1F];
|
||||
dest[2] = cb32[(src[1] >> 1) & 0x1F];
|
||||
dest[3] = cb32[((src[1] << 4) | (src[2] >> 4)) & 0x1F];
|
||||
dest[4] = cb32[((src[2] << 1) | (src[3] >> 7)) & 0x1F];
|
||||
dest[5] = cb32[(src[3] >> 2) & 0x1F];
|
||||
dest[6] = cb32[((src[3] << 3) | (src[4] >> 5)) & 0x1F];
|
||||
dest[7] = cb32[src[4] & 0x1F];
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief Decode a single base32-output group.
|
||||
* \param[in] src The encoded group of 8 base32 characters.
|
||||
* \param[out] dest The decoded group of 5 binary bytes.
|
||||
*
|
||||
* This function operates on a group of exactly 8 base32 symbols and
|
||||
* reconstructs the original 5 input bytes (8 × 5 = 40 bits = 5 × 8).
|
||||
*/
|
||||
static void unpack_from5(ut8 dest[5], const ut8 src[8]) {
|
||||
ut8 idx[8];
|
||||
size_t i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
idx[i] = cd32(src[i]);
|
||||
}
|
||||
dest[0] = (idx[0] << 3) | (idx[1] >> 2);
|
||||
dest[1] = (idx[1] << 6) | (idx[2] << 1) | (idx[3] >> 4);
|
||||
dest[2] = (idx[3] << 4) | (idx[4] >> 1);
|
||||
dest[3] = (idx[4] << 7) | (idx[5] << 2) | (idx[6] >> 3);
|
||||
dest[4] = (idx[6] << 5) | (idx[7] >> 0);
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief Validate a Base‑32 character.
|
||||
* \param c The character to examine (promoted to \c int by the caller).
|
||||
*
|
||||
* The function returns true in case:
|
||||
* - an upper‑case letter in the range <code>'A'–'Z'</code>, or
|
||||
* - a lower‑case letter in the range <code>'a'–'z'</code> (accepted for
|
||||
* leniency), or
|
||||
* - a digit in the range <code>'2'–'7'</code>.
|
||||
*
|
||||
* Any other value yields \c false.
|
||||
*/
|
||||
static inline bool is_base32(int c) {
|
||||
return (c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') || /* allow lower‑case */
|
||||
(c >= '2' && c <= '7');
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief Calculate the length of \p src.
|
||||
* \param[in] src The binary data to be Base32-encoded later.
|
||||
* \param len The length of the binary data in bytes.
|
||||
*
|
||||
* This function returns \p len as it is unless it is negative, in
|
||||
* which case, it returns the string length of \p src. A possibility
|
||||
* of a string size larger than \c ST64_MAX requires us to make a
|
||||
* bounds check, and error if overflow is possible. This function is
|
||||
* provided in lieu of modifying the decoding API parameter list.
|
||||
*/
|
||||
static st64 calculate_src_length(const char *src, st64 len) {
|
||||
if (len >= 0)
|
||||
return len;
|
||||
size_t real_len = strlen(src);
|
||||
if (ST64_MAX < real_len) {
|
||||
return -1;
|
||||
}
|
||||
return real_len;
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief Calculate the length, in bytes, of the Base‑32 encoded result.
|
||||
* \param len the length of the binary input in bytes.
|
||||
*
|
||||
* Base‑32 processes the data in 5‑byte blocks and produces an 8‑character
|
||||
* output block for each full 40‑bit chunk. When the input is not an exact
|
||||
* multiple of five bytes, the final (partial) block is still encoded as a
|
||||
* *full* 8‑character group, with the surplus characters subsequently replaced
|
||||
* by the ‘\c =’ padding symbol to reach the next 8‑byte boundary.
|
||||
*/
|
||||
static size_t calculate_dest_length(size_t len) {
|
||||
return 8 * ((len + 4) / 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Base32‑encode binary data.
|
||||
* \param[out] dest The encoded output.
|
||||
* \param[in] src The binary input data.
|
||||
* \param n The length of the binary data in bytes.
|
||||
* \return The length of the encoded output, excluding the NUL byte.
|
||||
* \attention The \p dest parameter must have enough space to hold the
|
||||
* full encoded output, including a terminating NUL byte. In particular,
|
||||
* it should reserve at least \c 1 + 8 × ((n + 4)/5) bytes.
|
||||
*
|
||||
* This function implements canonical Base‑32 encoding as per RFC 4648.
|
||||
* The input is padded with zero bits to a full 40‑bit block (5 bytes)
|
||||
* if needed. The output is a sequence of 8‑character blocks, padded
|
||||
* using the ‘\c =’ character to fill any final incomplete group.
|
||||
*
|
||||
* If \p dest or \p src is \c NULL, nothing is written and \c 0 is returned.
|
||||
*
|
||||
* # Example
|
||||
* \code{.c}
|
||||
* const ut8 msg[] = "Hi!";
|
||||
* size_t msg_len = strlen((const char *)msg);
|
||||
* size_t enc_len = 8 * ((msg_len + 4) / 5);
|
||||
* char *enc = malloc(enc_len + 1); // +1 for NUL byte
|
||||
* if (!enc) goto memory_error;
|
||||
* rz_base32_encode(enc, msg, msg_len);
|
||||
* assert(strcmp(enc, "JBUSC===") == 0);
|
||||
* free(enc);
|
||||
* \endcode
|
||||
*/
|
||||
RZ_API size_t rz_base32_encode(RZ_OUT RZ_NONNULL char *dest, RZ_NONNULL const ut8 *src, size_t n) {
|
||||
rz_return_val_if_fail(src && dest, 0);
|
||||
ut8 final_group[5] = { 0 };
|
||||
size_t ret = calculate_dest_length(n);
|
||||
while (n >= 5) {
|
||||
pack_to5((ut8 *)dest, src);
|
||||
src += 5;
|
||||
dest += 8;
|
||||
n -= 5;
|
||||
}
|
||||
if (n > 0) {
|
||||
memcpy(final_group, src, n);
|
||||
pack_to5((ut8 *)dest, final_group);
|
||||
static const ut8 pad_count[5] = { 6, 4, 3, 1 };
|
||||
size_t pad = pad_count[n - 1];
|
||||
memset(dest + (8 - pad), '=', pad);
|
||||
dest += 8;
|
||||
}
|
||||
dest[0] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Base‑32‑encode binary data and return the result in a newly allocated buffer.
|
||||
* \param[in] src Pointer to the binary data to encode.
|
||||
* \param n the length of the binary data in bytes.
|
||||
* \return A pointer to a NUL‑terminated Base‑32 string allocated with
|
||||
* \c malloc, or \c NULL if \p src is \c NULL or a memory‑allocation
|
||||
* failure occurs. The caller is responsible for \c free()‑ing the
|
||||
* returned buffer.
|
||||
*
|
||||
* This function is the Base‑32 analogue of \c rz_base64_encode_dyn. It first
|
||||
* computes the exact output size—\c 1 + 8 × ((n + 4)/5) bytes to accommodate
|
||||
* complete 8‑character blocks plus a terminating NUL—allocates that much
|
||||
* memory, and then invokes \c rz_base32_encode to fill the buffer.
|
||||
*
|
||||
* # Example
|
||||
* \code{.c}
|
||||
* const ut8 plain[] = { 0x48, 0x69 }; // "Hi"
|
||||
* char *enc = rz_base32_encode_dyn(plain, sizeof plain);
|
||||
* if (!enc) { goto memory_error; }
|
||||
* assert(strcmp(enc, "JBUSC===") == 0);
|
||||
* free(enc);
|
||||
* \endcode
|
||||
*/
|
||||
RZ_API RZ_OWN char *rz_base32_encode_dyn(RZ_NONNULL const ut8 *src, size_t n) {
|
||||
rz_return_val_if_fail(src, NULL);
|
||||
size_t buf_sz = calculate_dest_length(n) + 1;
|
||||
char *out = (char *)malloc(buf_sz);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
if (rz_base32_encode(out, src, n) == 0) {
|
||||
return NULL;
|
||||
}
|
||||
out[buf_sz - 1] = '\0';
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Decode a Base32-encoded message.
|
||||
* \param[out] dest The decoded output.
|
||||
* \param[in] src The Base32-encoded message.
|
||||
* \param n The length of the encoded message.
|
||||
* \return The length of the decoded message, excluding the NUL byte.
|
||||
* \attention The \p dest parameter should have sufficient space to
|
||||
* accomodate the decoded output, including the NUL byte. In
|
||||
* particular, it should have at least \c 1+(5*(n+4))/8 bytes
|
||||
* available.
|
||||
*
|
||||
* Decode a Base32-encoded message. The \p n parameter may be
|
||||
* negative, in which case \p src is treated as a C string and its
|
||||
* string length is calculated via strlen. The decoded output is stored in \p
|
||||
* dest, and will be NUL byte terminated.
|
||||
*
|
||||
* If either \p dest or \p src is \c NULL, nothing is done and the
|
||||
* value \c 0 is returned.
|
||||
*
|
||||
* The return value is \c -1 in the following cases:
|
||||
* - Characters in \p src are rejected as invalid.
|
||||
* - The parameter \p n was \c -1 and the string length exceeds \c ST64_MAX.
|
||||
*
|
||||
* # Example
|
||||
*
|
||||
* \code{.c}
|
||||
* const char *b32 = "MZXW6==="; // "foo"
|
||||
* ut8 buf[16];
|
||||
* st64 len = rz_base32_decode(buf, b32, -1);
|
||||
* assert(len == 3);
|
||||
* assert(memcmp(buf, "foo", 3) == 0);
|
||||
* \endcode
|
||||
*/
|
||||
RZ_API st64 rz_base32_decode(RZ_OUT RZ_NONNULL ut8 *dest, RZ_NONNULL const char *src, st64 n) {
|
||||
rz_return_val_if_fail(src && dest, 0);
|
||||
|
||||
char buf[8] = { 0 }, tmp[5] = { 0 };
|
||||
int c = 0;
|
||||
size_t i = 0, j = 0;
|
||||
st64 ret = 0;
|
||||
|
||||
n = calculate_src_length(src, n);
|
||||
if (n == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = j = 0; i < (size_t)n; i++) {
|
||||
c = src[i];
|
||||
if (is_base32(c)) {
|
||||
buf[j++] = c;
|
||||
if (j == 8) {
|
||||
j = 0;
|
||||
ret += 5;
|
||||
unpack_from5((ut8 *)dest, (const ut8 *)buf);
|
||||
dest += 5;
|
||||
}
|
||||
} else if (c == '=') {
|
||||
continue;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (j == 0) {
|
||||
dest[0] = '\0';
|
||||
return ret;
|
||||
} else if (j <= 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t rem = j;
|
||||
for (; j < 8; j++) {
|
||||
buf[j] = 'A'; // zero padding for partial group
|
||||
}
|
||||
unpack_from5((ut8 *)tmp, (const ut8 *)buf);
|
||||
// decide how many of the unpacked bytes are valid based on how many base32 symbols you originally had.
|
||||
static const int rem2bytes[8] = { 0, 0, 1, 0, 2, 3, 0, 4 };
|
||||
int bytes = rem2bytes[rem];
|
||||
if (bytes == 0) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(dest, tmp, bytes);
|
||||
dest[bytes] = '\0';
|
||||
return ret + bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Dynamically decode a Base32‑encoded string.
|
||||
* \param[in] src Pointer to the Base32 text.
|
||||
* \param len The length of \p src; pass \c -1 to use \c strlen(src).
|
||||
* \return A newly allocated buffer holding the decoded binary data and
|
||||
* terminated with a NUL byte, or \c NULL on error.
|
||||
*
|
||||
* Memory is obtained with \c malloc and must be released with \c free
|
||||
* by the caller. The function returns \c NULL when:
|
||||
* - \p src is \c NULL,
|
||||
* - a length overflow would exceed \c ST64_MAX,
|
||||
* - a memory allocation fails, or
|
||||
* - the underlying decoder reports malformed input.
|
||||
*
|
||||
* The worst‑case decoded size for \p len bytes of Base32 is
|
||||
* \c 1 + 5 × ((len + 7)/8) bytes (the final “\c 1” is for the NUL terminator).
|
||||
* This bound is used to pre‑allocate the buffer and then \c realloc
|
||||
* trims it to the exact number of bytes actually produced.
|
||||
*/
|
||||
RZ_API RZ_OWN ut8 *rz_base32_decode_dyn(RZ_NONNULL const char *src, st64 len) {
|
||||
rz_return_val_if_fail(src, NULL);
|
||||
|
||||
len = calculate_src_length(src, len);
|
||||
if (len < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
st64 cap = 1 + (5 * ((len + 7) / 8));
|
||||
ut8 *buf = (ut8 *)malloc((size_t)cap);
|
||||
if (!buf) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
st64 out_len = rz_base32_decode(buf, src, len);
|
||||
if (out_len < 0) {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* NUL‑terminate and shrink to fit. */
|
||||
buf[out_len] = '\0';
|
||||
if (out_len + 1 < cap) {
|
||||
ut8 *tmp = (ut8 *)realloc(buf, (size_t)out_len + 1);
|
||||
if (tmp) {
|
||||
buf = tmp;
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
149
librz/util/base36.c
Normal file
149
librz/util/base36.c
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
// SPDX-FileCopyrightText: 2014-2020 abcSup <zifan.tan@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2025 Ahmed Ibrahim <a.ibrahim8686@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief Base36 encoding and decoding functions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <rz_types_base.h>
|
||||
#include <rz_util.h>
|
||||
|
||||
#define RZ_BASE36_BUFSZ 13
|
||||
|
||||
// Constants to convert ASCII to its base36 value
|
||||
static const char d32[] = "[\\]^_`abcd$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$efghijklmnopqrstuvwxyz{|}~";
|
||||
|
||||
// The powers of 36 up to the 13th for 64-bit values
|
||||
static const ut64 pow36[] = { 1, 36, 1296, 46656, 1679616, 60466176, 2176782336,
|
||||
78364164096, 2821109907456, 101559956668416, 3656158440062976,
|
||||
131621703842267136, 4738381338321616896 };
|
||||
|
||||
/**
|
||||
* \brief Encode a 64-bit unsigned integer as a Base 36 digit string.
|
||||
*
|
||||
* \param[out] bout Buffer to receive the NUL-terminated encoded output.
|
||||
* Must have at least \c RZ_BASE36_BUFSZ bytes (\c 13 characters plus the terminator).
|
||||
* \param val The unsigned 64-bit integer to encode.
|
||||
* \return Number of characters written (excluding the NUL terminator), or \c 0 if \p bout is \c NULL.
|
||||
*
|
||||
* This function converts \p val to its lowercase Base 36 representation,
|
||||
* writing the result to \p bout followed by a NUL terminator.
|
||||
* The most-significant digit appears first in the output.
|
||||
*/
|
||||
RZ_API size_t rz_base36_encode(RZ_OUT RZ_NONNULL char *bout, ut64 val) {
|
||||
rz_return_val_if_fail(bout, 0);
|
||||
static const char alphabet[] = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
char tmp[RZ_BASE36_BUFSZ];
|
||||
size_t n = 0;
|
||||
|
||||
if (val == 0) {
|
||||
tmp[n++] = '0';
|
||||
} else {
|
||||
while (val && n < RZ_BASE36_BUFSZ) {
|
||||
tmp[n++] = alphabet[val % 36];
|
||||
val /= 36;
|
||||
}
|
||||
}
|
||||
|
||||
// reverse into output buffer
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
bout[i] = tmp[n - 1 - i];
|
||||
}
|
||||
bout[n] = '\0';
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Dynamically allocate and return the Base 36 representation of a 64‑bit value.
|
||||
*
|
||||
* \param val The unsigned 64‑bit integer to encode.
|
||||
* \return Pointer to a freshly allocated, NUL‑terminated digit string,
|
||||
* or \c NULL if memory allocation fails.
|
||||
*/
|
||||
RZ_API RZ_OWN char *rz_base36_encode_dyn(ut64 val) {
|
||||
char *out = (char *)malloc(RZ_BASE36_BUFSZ + 1);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
if (rz_base36_encode(out, val) == 0) {
|
||||
free(out);
|
||||
return NULL;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Decode a Base36 string into a 64-bit unsigned integer.
|
||||
*
|
||||
* \param[out] bout Pointer to a ut64 that will receive the decoded value.
|
||||
* \param[in] bin Input Base36-encoded string (not necessarily NUL-terminated).
|
||||
* \param[in] len Length of the Base36 string.
|
||||
* \return Number of characters consumed on success, or -1 on error.
|
||||
*
|
||||
* This function decodes the given Base36 string \p bin of length \p len into
|
||||
* a 64-bit unsigned integer. The maximum supported length is
|
||||
* \c RZ_BASE36_BUFSZ (13 characters). Input is case-sensitive and only
|
||||
* lowercase 'a'–'z' and '0'–'9' are accepted.
|
||||
*/
|
||||
RZ_API st64 rz_base36_decode(RZ_OUT RZ_NONNULL ut64 *bout, RZ_NONNULL const char *bin, st64 len) {
|
||||
rz_return_val_if_fail(bin && bout, -1);
|
||||
size_t i;
|
||||
// 64-bit base36 str has at most 13 characters
|
||||
if (len > RZ_BASE36_BUFSZ) {
|
||||
RZ_LOG_ERROR("base36_decode supports up to 64-bit values only\n");
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < len; i++) {
|
||||
char c = bin[len - i - 1];
|
||||
// "01234567890abcdefghijklmnopqrstuvwxyz"
|
||||
if (c < '0' || c > 'z' || ('9' < c && c < 'a')) {
|
||||
RZ_LOG_ERROR("%s is not a valid base36 encoded string\n", bin);
|
||||
return -1;
|
||||
}
|
||||
ut8 v = d32[c - '0'];
|
||||
// Character does not exist in base36 encoding
|
||||
if (v == '$') {
|
||||
RZ_LOG_ERROR("Error: %s is not a valid base36 encoded string\n", bin);
|
||||
return -1;
|
||||
}
|
||||
v -= 91;
|
||||
// Check for overflow
|
||||
if (i == 12) {
|
||||
if (v > 3 || UT64_ADD_OVFCHK(*bout, v * pow36[i])) {
|
||||
RZ_LOG_ERROR("Error: base36_decode supports up to 64-bit values only\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
*bout += v * pow36[i];
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Decode a Base36 string into a newly allocated 64-bit integer.
|
||||
*
|
||||
* \param[in] bin Base36-encoded string (not necessarily NUL-terminated).
|
||||
* \param[in] len Length of the string.
|
||||
* \return A newly allocated \c ut64* holding the decoded value,
|
||||
* or \c NULL on error.
|
||||
*/
|
||||
RZ_API RZ_OWN ut64 *rz_base36_decode_dyn(RZ_NONNULL const char *bin, const size_t len) {
|
||||
rz_return_val_if_fail(bin, NULL);
|
||||
ut64 *out = RZ_NEW0(ut64);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (rz_base36_decode(out, bin, (st64)len) < 0) {
|
||||
free(out);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
|
@ -1,12 +1,14 @@
|
|||
// SPDX-FileCopyrightText: 2011 Remy Oukaour
|
||||
// SPDX-FileCopyrightText: 2017 pancake
|
||||
// SPDX-FileCopyrightText: 2025 Ahmed Ibrahim
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/*
|
||||
* ascii85 - Ascii85 encode/decode data and print to standard output
|
||||
* ascii85 - Ascii85 encode/decode data
|
||||
*
|
||||
* Copyright (C) 2011 Remy Oukaour
|
||||
* Updated by pancake in 2017
|
||||
* Updated by Ahmed Ibrahim in 2025
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
@ -30,142 +32,398 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <rz_types.h>
|
||||
#include <rz_util.h>
|
||||
|
||||
static int getc_nospace(FILE *f) {
|
||||
int c;
|
||||
while (isspace(c = getc(f))) {
|
||||
;
|
||||
/** \internal
|
||||
* \brief Return the next non‑whitespace character from an in‑memory buffer.
|
||||
*
|
||||
* \param src Pointer to the start of the Ascii 85 text.
|
||||
* \param len Total length of the buffer in bytes.
|
||||
* \param pos Pointer to the current cursor index; on return it is advanced
|
||||
* past the character that was delivered.
|
||||
*
|
||||
* \return The first non‑space character at or after \p *pos, or <tt>EOF</tt>
|
||||
* when the cursor reaches \p len. Whitespace is determined by the C library’s \c isspace() predicate.
|
||||
*
|
||||
*/
|
||||
static int getc_nospace_buf(const char *src, st64 len, st64 *pos) {
|
||||
while (*pos < len && isspace((unsigned char)src[*pos])) {
|
||||
(*pos)++;
|
||||
}
|
||||
return c;
|
||||
if (*pos >= len) {
|
||||
return EOF;
|
||||
}
|
||||
return (unsigned char)src[(*pos)++];
|
||||
}
|
||||
|
||||
static void putc_wrap(char c, int wrap, int *len) {
|
||||
if (wrap && *len >= wrap) {
|
||||
putchar('\n');
|
||||
*len = 0;
|
||||
/** \internal
|
||||
* \brief Append a character to an output buffer, inserting a newline when the wrap width is reached.
|
||||
*
|
||||
* \param d Pointer to the current write cursor inside the destination buffer.
|
||||
* The pointer is advanced as characters are written.
|
||||
* \param len Pointer to a running count of bytes already written; incremented by this function.
|
||||
* \param wrap Maximum printable‐character column before a automatic newline is inserted.
|
||||
* A value of \c 0 disables wrapping entirely.
|
||||
* \param col Pointer to the column counter (characters since the last newline); reset when a wrap occurs.
|
||||
* \param c The character to append.
|
||||
*
|
||||
* If \p wrap is non‑zero and the column counter referenced by \p col is
|
||||
* greater than or equal to \p wrap, the function first appends a newline
|
||||
* to the buffer and resets \p *col to 0. It then appends \p c, advancing \p *d, and increments both \p *len
|
||||
* and \p *col to reflect the newly written character.
|
||||
*/
|
||||
static void putc_wrap_buf(char **d, size_t *len, int wrap, int *col, char c) {
|
||||
if (wrap && *col >= wrap) {
|
||||
*(*d)++ = '\n';
|
||||
(*len)++;
|
||||
*col = 0;
|
||||
}
|
||||
putchar(c);
|
||||
*(*d)++ = c;
|
||||
(*len)++;
|
||||
(*col)++;
|
||||
}
|
||||
|
||||
static void encode_tuple(unsigned long tuple, int count, int wrap, int *plen, int y_abbr) {
|
||||
int i, lim;
|
||||
char out[5];
|
||||
/** \internal
|
||||
* \brief Encoding up to four input bytes and append their Ascii 85 representation to a buffer.
|
||||
*
|
||||
* \param tuple A big‑endian 32‑bit value holding the pending input bytes.
|
||||
* \param count Number of meaningful bytes inside \p tuple ( 1 – 4 ).
|
||||
* A value of 4 represents a full 32‑bit group; smaller values
|
||||
* occur only for the final, partial block at end‑of‑input.
|
||||
* \param wrap Maximum printable‑column width before a newline is inserted;
|
||||
* a value of 0 disables automatic wrapping.
|
||||
* \param d Pointer to the current write cursor inside the destination
|
||||
* buffer. The cursor is advanced as characters are written.
|
||||
* \param len Pointer to a running total of bytes written so far; incremented
|
||||
* by this function.
|
||||
* \param col Pointer to the current column counter (characters since the
|
||||
* last newline); reset to 0 whenever a wrap occurs.
|
||||
* \param y_abbr When non‑zero, emit the non‑standard abbreviation
|
||||
* <tt>'y'</tt> for four space characters (<tt>0x20202020</tt>).
|
||||
*
|
||||
* **Abbreviations**
|
||||
* - A tuple of 0 (<tt>count == 4</tt>) is emitted as a single <tt>'z'</tt>.
|
||||
* - With \p y_abbr ≠ 0, the tuple 0x20202020 (<tt>count == 4</tt>) is emitted
|
||||
* as a single <tt>'y'</tt> (Adobe extension).
|
||||
*
|
||||
* **General case**
|
||||
* Otherwise the 32‑bit value is converted to five base‑85 digits.
|
||||
* For partial final blocks only the leading <tt>count + 1</tt> digits are
|
||||
* appended, exactly matching the Adobe/ RFC 1924 padding rule.
|
||||
*/
|
||||
static void encode_tuple_buf(unsigned long tuple, int count, int wrap, char **d, size_t *len, int *col, int y_abbr) {
|
||||
if (tuple == 0 && count == 4) {
|
||||
putc_wrap('z', wrap, plen);
|
||||
putc_wrap_buf(d, len, wrap, col, 'z');
|
||||
} else if (tuple == 0x20202020 && count == 4 && y_abbr) {
|
||||
putc_wrap('y', wrap, plen);
|
||||
putc_wrap_buf(d, len, wrap, col, 'y');
|
||||
} else {
|
||||
for (i = 0; i < 5; i++) {
|
||||
out[i] = tuple % 85 + '!';
|
||||
char out[5];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
out[i] = (tuple % 85) + '!';
|
||||
tuple /= 85;
|
||||
}
|
||||
lim = 4 - count;
|
||||
for (i = 4; i >= lim; i--) {
|
||||
putc_wrap(out[i], wrap, plen);
|
||||
int lim = 4 - count;
|
||||
for (int i = 4; i >= lim; i--) {
|
||||
putc_wrap_buf(d, len, wrap, col, out[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RZ_API void rz_base85_decode_tuple(unsigned long tuple, int count) {
|
||||
int i;
|
||||
for (i = 1; i < count; i++) {
|
||||
putchar(tuple >> ((4 - i) * 8));
|
||||
/** \internal
|
||||
* \brief Calculate the buffer size required for an Ascii 85 encoding.
|
||||
*
|
||||
* \param n The length of the binary input in bytes.
|
||||
* \param delims Non‑zero if the encoding will be wrapped in the Adobe
|
||||
* delimiters <code>"<~"</code> and <code>"~>"</code>.
|
||||
* \param wrap Line‑wrap column width. A value of 0 disables wrapping.
|
||||
* \return The number of bytes needed to store the encoded text
|
||||
* *including* the terminating NUL.
|
||||
*
|
||||
* The formula derives from:
|
||||
*
|
||||
* * **Expansion** – Each full 4‑byte block produces 5 digits.
|
||||
* A partial final block of <i>r</i> ∈ {1,2,3} bytes produces <i>r + 1</i> digits.
|
||||
* * **Delimiters** – When \p delims ≠ 0, exactly four characters
|
||||
* (<tt><~</tt> and <tt>~></tt>) are added.
|
||||
* * **Line breaks** – If \p wrap > 0, the encoder inserts a newline
|
||||
* before every byte whose position would otherwise push the current
|
||||
* column counter beyond \p wrap. The number of newline characters is
|
||||
* therefore <code>(total_chars ? (total_chars – 1) / wrap : 0)</code>.
|
||||
*
|
||||
* Finally one extra byte is added for the NUL terminator.
|
||||
*/
|
||||
static size_t rz_base85_enc_buflen(size_t n, int delims, int wrap) {
|
||||
size_t full = n / 4;
|
||||
size_t rem = n % 4;
|
||||
size_t digits = 5 * full + (rem ? rem + 1 : 0);
|
||||
size_t chars = digits + (delims ? 4 : 0);
|
||||
if (wrap > 0 && chars > 0) {
|
||||
size_t newlines = (chars - 1) / (size_t)wrap;
|
||||
chars += newlines;
|
||||
}
|
||||
return chars + 1;
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief Append up to four bytes decoded from an Ascii 85 tuple to a buffer.
|
||||
*
|
||||
* \param tuple The 32‑bit value obtained by accumulating five base‑85 digits.
|
||||
* \param count Number of digits that were present in the source group
|
||||
* ( 2 – 5 ). A full group ( 5 digits ) produces four bytes,
|
||||
* while a partial final group produces \p count – 1 bytes.
|
||||
* \param d Pointer to the current write cursor inside the destination
|
||||
* buffer; advanced as bytes are written.
|
||||
* \param len Pointer to a running total of bytes written so far; incremented
|
||||
* by this function.
|
||||
*
|
||||
* The function starts with the most‑significant decoded byte and appends up to
|
||||
* four bytes to the buffer referenced by \p d.
|
||||
*/
|
||||
static void decode_tuple_buf(unsigned long tuple, size_t count, char **d, size_t *len) {
|
||||
for (size_t i = 1; i < count; i++) {
|
||||
*(*d)++ = (char)(tuple >> ((4 - i) * 8));
|
||||
(*len)++;
|
||||
}
|
||||
}
|
||||
|
||||
RZ_API void rz_base85_encode(FILE *fp, int delims, int wrap, int y_abbr) {
|
||||
int c, count = 0, len = 0;
|
||||
unsigned long tuple = 0;
|
||||
if (delims) {
|
||||
putc_wrap('<', wrap, &len);
|
||||
putc_wrap('~', wrap, &len);
|
||||
/** \internal
|
||||
* \brief Compute the buffer size required to hold the decoded bytes.
|
||||
*
|
||||
* \param enc_len The length, in characters, of the Ascii 85 text that will be
|
||||
* passed to the decoder <b>after</b> stripping any
|
||||
* delimiters, line‑breaks, or whitespace.
|
||||
*
|
||||
* \return A value large enough to store every possible decoding of a string
|
||||
* of length \p enc_len, <em>plus</em> one extra byte for a NUL
|
||||
* terminator.
|
||||
*/
|
||||
static size_t rz_base85_dec_buflen(size_t enc_len) {
|
||||
/* Upper bound: each Ascii85 char can produce up to 4 bytes, plus NUL */
|
||||
return 1 + 4 * enc_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Base-encode a memory buffer to Ascii 85.
|
||||
*
|
||||
* \param dest Destination buffer that receives the encoded text.
|
||||
* \param src Pointer to the binary data to encode.
|
||||
* \param n The length of \p src in bytes.
|
||||
* \param delims When non-zero, wrap the output between literal
|
||||
* <code>"<~"</code> and <code>"~>"</code>.
|
||||
* \param wrap Column width for automatic line wrapping.
|
||||
* A value of 0 disables wrapping.
|
||||
* \param y_abbr When non-zero, enable the non-standard abbreviation
|
||||
* <code>'y'</code> for the pattern 0x20 0x20 0x20 0x20.
|
||||
* \return The number of characters written to \p dest (excluding the
|
||||
* terminating NUL), or \c 0 if either \p dest or \p src is <code>NULL</code>.
|
||||
*
|
||||
* \attention The caller must allocate at least \c 1 + 5 × ((n + 3)/4) bytes,
|
||||
* plus room for optional delimiters and line-wrap newlines.
|
||||
*
|
||||
* The function processes the input four bytes at a time, converts each group
|
||||
* into five base-85 digits, and appends the result to \p dest. Special-case
|
||||
* abbreviations are emitted where possible:
|
||||
* - <code>'z'</code> for four zero bytes,
|
||||
* - <code>'y'</code> for four space bytes when \p y_abbr is true.
|
||||
*/
|
||||
RZ_API int rz_base85_encode(RZ_OUT RZ_NONNULL char *dest, RZ_NONNULL const char *src, size_t n, int delims, int wrap, int y_abbr) {
|
||||
if (!dest || !src) {
|
||||
return 0;
|
||||
}
|
||||
for (;;) {
|
||||
c = getc(fp);
|
||||
if (c != EOF) {
|
||||
tuple |= c << ((3 - count++) * 8);
|
||||
if (count < 4) {
|
||||
|
||||
char *d = dest;
|
||||
size_t out = 0;
|
||||
int col = 0;
|
||||
|
||||
if (delims) {
|
||||
putc_wrap_buf(&d, &out, wrap, &col, '<');
|
||||
putc_wrap_buf(&d, &out, wrap, &col, '~');
|
||||
}
|
||||
|
||||
unsigned long tuple = 0;
|
||||
int count = 0;
|
||||
|
||||
for (size_t i = 0; i < n || count > 0;) {
|
||||
if (i < n) {
|
||||
tuple |= (unsigned long)src[i++] << ((3 - count) * 8);
|
||||
if (++count < 4) {
|
||||
continue;
|
||||
}
|
||||
} else if (count == 0) {
|
||||
break;
|
||||
}
|
||||
encode_tuple(tuple, count, wrap, &len, y_abbr);
|
||||
if (c == EOF) {
|
||||
break;
|
||||
}
|
||||
encode_tuple_buf(tuple, count, wrap, &d, &out, &col, y_abbr);
|
||||
tuple = 0;
|
||||
count = 0;
|
||||
}
|
||||
if (delims) {
|
||||
putc_wrap('~', wrap, &len);
|
||||
putc_wrap('>', wrap, &len);
|
||||
putc_wrap_buf(&d, &out, wrap, &col, '~');
|
||||
putc_wrap_buf(&d, &out, wrap, &col, '>');
|
||||
}
|
||||
*d = '\0'; /* NUL‑terminate the C‑string */
|
||||
return (int)out;
|
||||
}
|
||||
|
||||
RZ_API bool rz_base85_decode(FILE *fp, int delims, int ignore_garbage) {
|
||||
int c, count = 0, end = 0;
|
||||
unsigned long tuple = 0, pows[] = { 85 * 85 * 85 * 85, 85 * 85 * 85, 85 * 85, 85, 1 };
|
||||
while (delims) {
|
||||
c = getc_nospace(fp);
|
||||
if (c == '<') {
|
||||
c = getc_nospace(fp);
|
||||
if (c == '~') {
|
||||
break;
|
||||
}
|
||||
ungetc(c, fp);
|
||||
} else if (c == EOF) {
|
||||
eprintf("ascii85: missing <~");
|
||||
return false;
|
||||
/**
|
||||
* \brief Dynamically allocate and return an Ascii 85 encoding.
|
||||
*
|
||||
* \param src Pointer to the binary data to encode.
|
||||
* \param n The length of \p src in bytes.
|
||||
* \param delims Non‑zero to surround the payload with <tt>"<~"</tt> … <tt>"~>"</tt>.
|
||||
* \param wrap Column width for automatic line wrapping (0 = none).
|
||||
* \param y_abbr Non‑zero to enable the <tt>'y'</tt> abbreviation for four spaces.
|
||||
*
|
||||
* \return Pointer to a freshly‑allocated, NUL‑terminated string containing
|
||||
* the Ascii 85 representation, or <code>NULL</code> if either
|
||||
* <code>src == NULL</code> or a memory allocation fails.
|
||||
*/
|
||||
RZ_API RZ_OWN char *rz_base85_encode_dyn(RZ_NONNULL const char *src, size_t n, int delims, int wrap, int y_abbr) {
|
||||
rz_return_val_if_fail(src, NULL);
|
||||
|
||||
char *ret = (char *)malloc(rz_base85_enc_buflen(n, delims, wrap));
|
||||
if (!ret) {
|
||||
return NULL;
|
||||
}
|
||||
rz_base85_encode(ret, src, n, delims, wrap, y_abbr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Decode an Ascii 85 string held in memory.
|
||||
*
|
||||
* \param[out] dest Destination buffer that receives the decoded bytes.
|
||||
* \param[in] src Pointer to the Ascii 85 text. Need not be NUL‑terminated.
|
||||
* \param len Number of characters in \p src; pass –1 to decode up to
|
||||
* the first <code>'\0'</code>.
|
||||
* \param delims When non‑zero, require the Adobe delimiters
|
||||
* <tt>"<~"</tt> … <tt>"~>"</tt>.
|
||||
* \param ignore_garbage When non‑zero, silently skip bytes outside the legal
|
||||
* digit range; otherwise such bytes trigger an error.
|
||||
*
|
||||
* \return The number of decoded bytes on success, or –1 on error
|
||||
* (malformed input or delimiter mismatch).
|
||||
*
|
||||
* \attention The caller must allocate \p dest large enough. A safe upper bound is
|
||||
* \c 1 + 3 × ((len + 1)/4) bytes.
|
||||
*/
|
||||
RZ_API st64 rz_base85_decode(RZ_OUT RZ_NONNULL char *dest, RZ_NONNULL const char *src, st64 len, int delims, int ignore_garbage) {
|
||||
rz_return_val_if_fail(dest, -1);
|
||||
rz_return_val_if_fail(src, -1);
|
||||
|
||||
if (len < 0) {
|
||||
len = (st64)strlen(src);
|
||||
}
|
||||
|
||||
st64 pos = 0;
|
||||
int count = 0;
|
||||
int have_end = 0;
|
||||
size_t out = 0;
|
||||
|
||||
unsigned long tuple = 0;
|
||||
const unsigned long pows[5] = {
|
||||
85u * 85u * 85u * 85u,
|
||||
85u * 85u * 85u,
|
||||
85u * 85u,
|
||||
85u,
|
||||
1u
|
||||
};
|
||||
|
||||
if (delims) {
|
||||
int c = getc_nospace_buf(src, len, &pos);
|
||||
if (c != '<' || getc_nospace_buf(src, len, &pos) != '~') {
|
||||
RZ_LOG_ERROR("ascii85: missing <~\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
c = getc_nospace(fp);
|
||||
int c = getc_nospace_buf(src, len, &pos);
|
||||
if (c == 'z' && count == 0) {
|
||||
rz_base85_decode_tuple(0, 5);
|
||||
decode_tuple_buf(0, 5, &dest, &out);
|
||||
continue;
|
||||
}
|
||||
if (c == 'y' && count == 0) {
|
||||
rz_base85_decode_tuple(0x20202020, 5);
|
||||
decode_tuple_buf(0x20202020, 5, &dest, &out);
|
||||
continue;
|
||||
}
|
||||
if (c == '~' && delims) {
|
||||
c = getc_nospace(fp);
|
||||
if (c != '>') {
|
||||
eprintf("ascii85: ~ without >\n");
|
||||
return false;
|
||||
if (getc_nospace_buf(src, len, &pos) != '>') {
|
||||
RZ_LOG_ERROR("ascii85: '~' not followed by '>'\n");
|
||||
return -1;
|
||||
}
|
||||
c = EOF;
|
||||
end = 1;
|
||||
have_end = 1;
|
||||
break;
|
||||
}
|
||||
if (c == EOF) {
|
||||
if (delims && !end) {
|
||||
eprintf("ascii85: missing ~>");
|
||||
return false;
|
||||
}
|
||||
if (count > 0) {
|
||||
tuple += pows[count - 1];
|
||||
rz_base85_decode_tuple(tuple, count);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (c < '!' || c > 'u') {
|
||||
if (ignore_garbage) {
|
||||
continue;
|
||||
}
|
||||
eprintf("ascii85: invalid character '%c'\n", c);
|
||||
return false;
|
||||
RZ_LOG_ERROR("ascii85: invalid character '%c'\n", c);
|
||||
return -1;
|
||||
}
|
||||
tuple += (c - '!') * pows[count++];
|
||||
tuple += (unsigned long)(c - '!') * pows[count++];
|
||||
if (count == 5) {
|
||||
rz_base85_decode_tuple(tuple, count);
|
||||
decode_tuple_buf(tuple, count, &dest, &out);
|
||||
tuple = 0;
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
if (delims && !have_end) {
|
||||
RZ_LOG_ERROR("ascii85: missing ~>\n");
|
||||
return -1;
|
||||
}
|
||||
if (count > 0) { /* partial final group */
|
||||
tuple += pows[count - 1]; /* implicit 'u' padding */
|
||||
decode_tuple_buf(tuple, count, &dest, &out);
|
||||
}
|
||||
return (st64)out;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Decode an Ascii 85 string, allocating the output buffer automatically.
|
||||
*
|
||||
* \param src Ascii 85 text to decode (may contain NUL‑bytes).
|
||||
* \param len The length of \p src in bytes, or –1 to use strlen().
|
||||
* \param delims Expect <tt>"<~"</tt> … <tt>"~>"</tt> delimiters.
|
||||
* \param ignore_garbage Skip vs. error on invalid characters.
|
||||
* \param[out] out_len Optional pointer that receives the decoded size.
|
||||
*
|
||||
* \return Pointer to a newly allocated buffer containing the raw bytes,
|
||||
* or \c NULL on error. The buffer is NUL‑terminated for convenience.
|
||||
*/
|
||||
RZ_API RZ_OWN char *rz_base85_decode_dyn(RZ_NONNULL const char *src, st64 len, int delims, int ignore_garbage, size_t *out_len) {
|
||||
rz_return_val_if_fail(src, NULL);
|
||||
|
||||
if (len < 0) {
|
||||
len = (st64)strlen(src);
|
||||
}
|
||||
|
||||
size_t cap = rz_base85_dec_buflen(len);
|
||||
char *buf = (char *)malloc(cap);
|
||||
if (!buf) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
st64 written = rz_base85_decode(buf, src, len, delims, ignore_garbage);
|
||||
if (written < 0) {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* shrink‑to‑fit and NUL‑terminate */
|
||||
if (written + 1 < (st64)cap) {
|
||||
char *tmp = (char *)realloc(buf, (size_t)written + 1);
|
||||
if (tmp) {
|
||||
buf = tmp;
|
||||
}
|
||||
}
|
||||
buf[written] = '\0';
|
||||
if (out_len) {
|
||||
*out_len = (size_t)written;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -456,6 +456,19 @@ RZ_API int rz_hex_pair2bin(const char *arg) {
|
|||
return (int)c;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Convert binary data to a lowercase hexadecimal string.
|
||||
* \param[in] in Pointer to the binary input.
|
||||
* \param[in] len Number of bytes in \p in. Must be non-negative.
|
||||
* \param[out] out Buffer to receive NUL-terminated hexadecimal output.
|
||||
* Must have at least \c (2 × len) + 1 bytes.
|
||||
* \return Number of bytes processed (same as \p len), or \c 0 if \p len
|
||||
* is negative.
|
||||
*
|
||||
* This function encodes each input byte as two lowercase hexadecimal
|
||||
* characters (e.g., \c 0xAF becomes "af") using \c snprintf() and writes
|
||||
* them sequentially to the output buffer, followed by a NUL terminator.
|
||||
*/
|
||||
RZ_API int rz_hex_bin2str(const ut8 *in, int len, char *out) {
|
||||
int i, idx;
|
||||
char tmp[8];
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ rz_util_common_sources = [
|
|||
'assert.c',
|
||||
'astr.c',
|
||||
'axml.c',
|
||||
'base85.c',
|
||||
'base91.c',
|
||||
'bitvector.c',
|
||||
'buf.c',
|
||||
'calc.c',
|
||||
|
|
@ -73,7 +71,12 @@ rz_util_common_sources = [
|
|||
'thread_types.c',
|
||||
'time.c',
|
||||
'tree.c',
|
||||
'ubase64.c',
|
||||
'base16.c',
|
||||
'base32.c',
|
||||
'base36.c',
|
||||
'base64.c',
|
||||
'base85.c',
|
||||
'base91.c',
|
||||
'uleb128.c',
|
||||
'unum.c',
|
||||
'utf16.c',
|
||||
|
|
|
|||
|
|
@ -39,48 +39,6 @@ typedef struct iobnet_t {
|
|||
RzCrypto *crypto;
|
||||
} iobnet_t;
|
||||
|
||||
// Constants to convert ASCII to its base36 value
|
||||
static const char d32[] = "[\\]^_`abcd$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$efghijklmnopqrstuvwxyz{|}~";
|
||||
// The powers of 36 up to the 13th for 64-bit values
|
||||
static const ut64 pow36[] = { 1, 36, 1296, 46656, 1679616, 60466176, 2176782336,
|
||||
78364164096, 2821109907456, 101559956668416, 3656158440062976,
|
||||
131621703842267136, 4738381338321616896 };
|
||||
|
||||
static ut64 base36_decode(const char *str) {
|
||||
ut64 ret = 0;
|
||||
size_t i;
|
||||
size_t len = strlen(str);
|
||||
// 64-bit base36 str has at most 13 characters
|
||||
if (len > 13) {
|
||||
eprintf("Error: base36_decode supports up to 64-bit values only\n");
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < len; i++) {
|
||||
char c = str[len - i - 1];
|
||||
// "01234567890abcdefghijklmnopqrstuvwxyz"
|
||||
if (c < '0' || c > 'z' || ('9' < c && c < 'a')) {
|
||||
eprintf("Error: %s is not a valid base36 encoded string\n", str);
|
||||
return 0;
|
||||
}
|
||||
ut8 v = d32[c - '0'];
|
||||
// Character does not exist in base36 encoding
|
||||
if (v == '$') {
|
||||
eprintf("Error: %s is not a valid base36 encoded string\n", str);
|
||||
return 0;
|
||||
}
|
||||
v -= 91;
|
||||
// Check for overflow
|
||||
if (i == 12) {
|
||||
if (v > 3 || UT64_ADD_OVFCHK(ret, v * pow36[i])) {
|
||||
printf("Error: base36_decode supports up to 64-bit values only\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ret += v * pow36[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Initialize the key for enc/decrypting KDNet packet with the type Data.
|
||||
*
|
||||
|
|
@ -147,7 +105,11 @@ static void *iob_net_open(const char *path) {
|
|||
if (nkey) {
|
||||
*nkey++ = 0;
|
||||
}
|
||||
rz_write_le64(obj->key + i * 8, base36_decode(key));
|
||||
|
||||
ut64 decoded_val = 0;
|
||||
st64 ret = rz_base36_decode(&decoded_val, key, strlen(key));
|
||||
decoded_val = ret >= 0 ? decoded_val : 0;
|
||||
rz_write_le64(obj->key + i * 8, (ut64)decoded_val);
|
||||
}
|
||||
|
||||
// HMAC Key is the negation of AES-256 Control Key bytes
|
||||
|
|
|
|||
|
|
@ -282,16 +282,20 @@ ED____ rc4 LGPL-3 pancake RC4 symmetric-ke
|
|||
ED____ rc6 LGPL-3 rakholiyajenish.07 RC6 symmetric-key block cipher
|
||||
_D____ ror LGPL-3 pancake Rotate Right symmetric-key block cipher
|
||||
__ed__ base64 LGPL-3 rakholiyajenish.07 Base64 encoder/decoder
|
||||
ED____ xor LGPL-3 pancake XOR symmetric-key block cipher
|
||||
__ed__ base32 LGPL-3 Ahmed Ibrahim Base32 encoder/decoder
|
||||
ED____ rc2 LGPL-3 lionaneesh RC2 symmetric-key block cipher
|
||||
ED____ xor LGPL-3 pancake XOR symmetric-key block cipher
|
||||
ED____ cps2 LGPL-3 pancake,esanfelix,pof Capcom Play System 2 (CPS-2) symmetric-key block cipher
|
||||
ED____ rot LGPL-3 pancake Caesar symmetric-key cipher
|
||||
__ed__ punycode LGPL-3 pancake Punycode encoder/decoder
|
||||
ED____ serpent-ecb LGPL-3 NicsTr Serpent symmetric-key block cipher (ECB block mode)
|
||||
ED____ sm4-ecb LGPL-3 0xSh4dy ShangMi 4 symmetric-key block cipher (ECB block mode)
|
||||
__ed__ base36 LGPL-3 abcSup Base36 encoder/decoder
|
||||
__ed__ base85 LGPL-3 Ahmed Ibrahim Base85 encoder/decoder
|
||||
ED____ aes-cbc LGPL-3 rakholiyajenish.07 AES symmetric-key block cipher (CBC block mode)
|
||||
E_____ rol LGPL-3 pancake Rotate Left symmetric-key block cipher
|
||||
ED____ aes-ecb LGPL3 Nettle project,pancake AES symmetric-key block cipher (ECB block mode)
|
||||
__ed__ base16 LGPL-3 Ahmed Ibrahim Base16 encoder/decoder
|
||||
ED____ des-ecb LGPL-3 deroad DES symmetric-key block cipher (ECB block mode)
|
||||
ED____ blowfish LGPL3 kishorbhat Blowfish symmetric-key block cipher
|
||||
__ed__ base91 LGPL-3 rakholiyajenish.07 Base91 encoder/decoder
|
||||
|
|
|
|||
|
|
@ -23,7 +23,11 @@ if get_option('enable_tests')
|
|||
'analysis_var',
|
||||
'analysis_xrefs',
|
||||
'annotated_code',
|
||||
'base16',
|
||||
'base32',
|
||||
'base36',
|
||||
'base64',
|
||||
'base85',
|
||||
'big',
|
||||
'bin_lines',
|
||||
'bin_mach0',
|
||||
|
|
|
|||
64
test/unit/test_base16.c
Normal file
64
test/unit/test_base16.c
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// SPDX-FileCopyrightText: 2025 Ahmed Ibrahim <a.ibrahim8686@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_util.h>
|
||||
#include "minunit.h"
|
||||
|
||||
bool test_rz_base16_decode_dyn(void) {
|
||||
char *hello = (char *)rz_base16_decode_dyn("68656c6c6f", -1);
|
||||
mu_assert_streq(hello, "hello", "base16_decode_dyn");
|
||||
free(hello);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base16_decode_even(void) {
|
||||
ut8 buf[16];
|
||||
int status = rz_base16_decode(buf, "68656c6c6f");
|
||||
mu_assert_eq(status, 5, "even-length hex decoded length mismatch");
|
||||
mu_assert_memeq(buf, (const ut8 *)"hello", 5, "even-length hex decoded bytes mismatch");
|
||||
mu_assert_eq(buf[5], '\0', "missing NUL terminator");
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base16_decode_odd(void) {
|
||||
ut8 buf[16];
|
||||
int status = rz_base16_decode(buf, "68 65 6c 6c 6f 6");
|
||||
mu_assert_eq(status, -6, "odd-length hex did not return negative length");
|
||||
mu_assert_memeq(buf, (const ut8 *)"hello\x60", 6, "odd-length hex decoded bytes mismatch");
|
||||
mu_assert_eq(buf[6], '\0', "missing NUL terminator");
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base16_decode_invalid(void) {
|
||||
ut8 buf[16];
|
||||
int status = rz_base16_decode(buf, "68656cZZ");
|
||||
mu_assert_eq(status, 0, "invalid char should return 0");
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base16_encode_dyn(void) {
|
||||
char *enc = rz_base16_encode_dyn((const ut8 *)"hello", 5);
|
||||
mu_assert_streq(enc, "68656c6c6f", "encode_dyn mismatch");
|
||||
free(enc);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base16_encode(void) {
|
||||
char enc[16];
|
||||
rz_base16_encode(enc, (const ut8 *)"hello", 5);
|
||||
mu_assert_streq(enc, "68656c6c6f", "encode mismatch");
|
||||
mu_end;
|
||||
}
|
||||
|
||||
int all_tests() {
|
||||
mu_run_test(test_rz_base16_decode_dyn);
|
||||
mu_run_test(test_rz_base16_decode_even);
|
||||
mu_run_test(test_rz_base16_decode_odd);
|
||||
mu_run_test(test_rz_base16_decode_invalid);
|
||||
mu_run_test(test_rz_base16_encode_dyn);
|
||||
mu_run_test(test_rz_base16_encode);
|
||||
|
||||
return tests_passed != tests_run;
|
||||
}
|
||||
|
||||
mu_main(all_tests)
|
||||
75
test/unit/test_base32.c
Normal file
75
test/unit/test_base32.c
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// SPDX-FileCopyrightText: 2025 Ahmed Ibrahim <a.ibrahim8686@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_util.h>
|
||||
#include "minunit.h"
|
||||
|
||||
bool test_rz_base32_decode_dyn(void) {
|
||||
char *hello = (char *)rz_base32_decode_dyn("NBSWY3DP", -1);
|
||||
mu_assert_streq(hello, "hello", "base32_decode_dyn");
|
||||
free(hello);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base32_decode(void) {
|
||||
ut8 *hello = malloc(50);
|
||||
int status = rz_base32_decode(hello, "NBSWY3DP", -1);
|
||||
mu_assert_eq(status, (int)strlen("hello"), "valid base32 decoding");
|
||||
mu_assert_streq((char *)hello, "hello", "base32 decoding");
|
||||
free(hello);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base32_decode_invalid(void) {
|
||||
ut8 buf[16];
|
||||
mu_assert_eq(rz_base32_decode(buf, "MZXW@===", -1), -1, "decoder accepted invalid char");
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base32_encode_dyn(void) {
|
||||
static const struct {
|
||||
const char *in;
|
||||
const char *exp;
|
||||
} vec[] = {
|
||||
{ "hello", "NBSWY3DP" },
|
||||
{ "hello1", "NBSWY3DPGE======" },
|
||||
{ "hello12", "NBSWY3DPGEZA====" },
|
||||
{ "hello123", "NBSWY3DPGEZDG===" }
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < RZ_ARRAY_SIZE(vec); i++) {
|
||||
char *enc = rz_base32_encode_dyn((const ut8 *)vec[i].in, strlen(vec[i].in));
|
||||
mu_assert_streq(enc, vec[i].exp, "encode_dyn mismatch");
|
||||
free(enc);
|
||||
}
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base32_encode(void) {
|
||||
char enc[32];
|
||||
rz_base32_encode(enc, (const ut8 *)"hello", 5);
|
||||
mu_assert_streq(enc, "NBSWY3DP", "encode mismatch");
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base32_decode_offby1(void) {
|
||||
unsigned char msg[4] = { 'A', 0, 'B', 0 };
|
||||
char enc[32] = { 0 };
|
||||
|
||||
rz_base32_encode(enc, msg, 1);
|
||||
rz_base32_decode(msg, enc, strlen(enc));
|
||||
mu_assert_eq(msg[2], 'B', "decoder wrote past end");
|
||||
mu_end;
|
||||
}
|
||||
|
||||
int all_tests() {
|
||||
mu_run_test(test_rz_base32_decode_dyn);
|
||||
mu_run_test(test_rz_base32_decode);
|
||||
mu_run_test(test_rz_base32_decode_invalid);
|
||||
mu_run_test(test_rz_base32_encode_dyn);
|
||||
mu_run_test(test_rz_base32_encode);
|
||||
mu_run_test(test_rz_base32_decode_offby1);
|
||||
return tests_passed != tests_run;
|
||||
}
|
||||
|
||||
mu_main(all_tests)
|
||||
92
test/unit/test_base36.c
Normal file
92
test/unit/test_base36.c
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
// SPDX-FileCopyrightText: 2025 Ahmed Ibrahim <a.ibrahim8686@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <limits.h>
|
||||
#include <rz_util.h>
|
||||
#include "minunit.h"
|
||||
|
||||
static const struct {
|
||||
ut64 value;
|
||||
const char *str;
|
||||
} vectors[] = {
|
||||
{ 0ULL, "0" },
|
||||
{ 1ULL, "1" },
|
||||
{ 35ULL, "z" },
|
||||
{ 36ULL, "10" },
|
||||
{ 125ULL, "3h" },
|
||||
{ 123456789ULL, "21i3v9" },
|
||||
{ UINT64_MAX, "3w5e11264sgsf" }
|
||||
};
|
||||
|
||||
bool test_rz_base36_encode_dyn_basic(void) {
|
||||
for (size_t i = 0; i < RZ_ARRAY_SIZE(vectors); i++) {
|
||||
char *s = rz_base36_encode_dyn(vectors[i].value);
|
||||
mu_assert_notnull(s, "encode returned NULL");
|
||||
mu_assert_streq(s, vectors[i].str, "unexpected base36 encoding");
|
||||
free(s);
|
||||
}
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base36_decode_basic(void) {
|
||||
for (size_t i = 0; i < RZ_ARRAY_SIZE(vectors); i++) {
|
||||
ut64 out = 0;
|
||||
ut64 ret = rz_base36_decode(&out, vectors[i].str, strlen(vectors[i].str));
|
||||
mu_assert_neq(ret, -1, "decode failed unexpectedly");
|
||||
mu_assert_eq(out, vectors[i].value, "base36 decode mismatch");
|
||||
}
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base36_roundtrip_large(void) {
|
||||
const ut64 x = 0xDEADBEEFCAFEBABEULL; /* arbitrary 64‑bit value */
|
||||
char *enc = rz_base36_encode_dyn(x);
|
||||
mu_assert_notnull(enc, "encode_dyn returned NULL");
|
||||
|
||||
ut64 out = 0;
|
||||
st64 ret = rz_base36_decode(&out, enc, strlen(enc));
|
||||
free(enc);
|
||||
|
||||
mu_assert_neq(ret, -1, "decode failed unexpectedly");
|
||||
mu_assert_eq(out, x, "round‑trip encode/decode failed");
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base36_decode_invalid_char(void) {
|
||||
/* '@' is not legal in [0‑9A‑Z] */
|
||||
const char bad[] = "1a@";
|
||||
ut64 out = 0;
|
||||
st64 ret = rz_base36_decode(&out, bad, strlen(bad));
|
||||
mu_assert_eq(ret, -1, "decoder did not flag invalid char");
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base36_decode_overflow(void) {
|
||||
/* "zzzzzzzzzzzzzz" (14×'z') = 36^14-1 > 2^64-1 */
|
||||
const char huge[] = "zzzzzzzzzzzzzz";
|
||||
ut64 out = 0;
|
||||
ut64 ret = rz_base36_decode(&out, huge, strlen(huge));
|
||||
mu_assert_eq(ret, -1, "decoder did not flag overflow");
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base36_decode_dyn(void) {
|
||||
ut64 *out = rz_base36_decode_dyn("21i3v9", 6);
|
||||
mu_assert_notnull(out, "decode_dyn returned NULL");
|
||||
mu_assert_eq(*out, 123456789ULL, "decode_dyn mismatch");
|
||||
free(out);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
int all_tests(void) {
|
||||
mu_run_test(test_rz_base36_encode_dyn_basic);
|
||||
mu_run_test(test_rz_base36_decode_basic);
|
||||
mu_run_test(test_rz_base36_roundtrip_large);
|
||||
mu_run_test(test_rz_base36_decode_invalid_char);
|
||||
mu_run_test(test_rz_base36_decode_overflow);
|
||||
mu_run_test(test_rz_base36_decode_dyn);
|
||||
|
||||
return tests_passed != tests_run;
|
||||
}
|
||||
|
||||
mu_main(all_tests)
|
||||
119
test/unit/test_base85.c
Normal file
119
test/unit/test_base85.c
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
// SPDX-FileCopyrightText: 2025 Ahmed Ibrahim <a.ibrahim8686@gmail.com>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_util.h>
|
||||
#include "minunit.h"
|
||||
|
||||
bool test_rz_base85_encode_dyn_nodelims(void) {
|
||||
const char *src = "hello, ascii85";
|
||||
char *dest = rz_base85_encode_dyn(src, strlen(src), 0, 0, 1);
|
||||
mu_assert_streq(dest, "BOu!rD_*#>F(8ou3&L", "ascii85 encode mismatch");
|
||||
free(dest);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base85_encode_dyn_delims(void) {
|
||||
const char *src = "hello, ascii85";
|
||||
char *dest = rz_base85_encode_dyn(src, strlen(src), 1, 0, 1);
|
||||
mu_assert_streq(dest, "<~BOu!rD_*#>F(8ou3&L~>", "ascii85 encode mismatch");
|
||||
free(dest);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base85_decode_dyn_nodelims(void) {
|
||||
const char *src = "BOu!rD_*#>F(8ou3&L";
|
||||
char *dest = rz_base85_decode_dyn(src, strlen(src), 0, 0, NULL);
|
||||
mu_assert_streq(dest, "hello, ascii85", "decoder output mismatch");
|
||||
free(dest);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base85_decode_dyn_delims(void) {
|
||||
const char *src = "<~BOu!rD_*#>F(8ou3&L~>";
|
||||
char *dest = rz_base85_decode_dyn(src, strlen(src), 1, 0, NULL);
|
||||
mu_assert_streq(dest, "hello, ascii85", "ascii85 decode with delimiters mismatch");
|
||||
free(dest);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base85_encode_decode_wrap10(void) {
|
||||
char src[51];
|
||||
memset(src, 'A', 50);
|
||||
src[50] = '\0';
|
||||
|
||||
char *encoded = rz_base85_encode_dyn(src, strlen(src), 0, 10, 0);
|
||||
|
||||
int col = 0, newlines = 0;
|
||||
for (size_t i = 0; encoded[i]; i++) {
|
||||
if (encoded[i] == '\n') {
|
||||
mu_assert_eq(col, 10, "line length before \\n not 10");
|
||||
col = 0;
|
||||
newlines++;
|
||||
} else {
|
||||
col++;
|
||||
}
|
||||
}
|
||||
mu_assert_true(col <= 10, "final line exceeds wrap length");
|
||||
mu_assert_true(newlines > 0, "no line breaks were inserted");
|
||||
|
||||
size_t decoded_len;
|
||||
char *decoded = rz_base85_decode_dyn(encoded, strlen(encoded), 0, 0, &decoded_len);
|
||||
|
||||
mu_assert_eq(decoded_len, 50U, "decoded length mismatch");
|
||||
mu_assert_true(memcmp(decoded, src, 50) == 0, "decoded data mismatch");
|
||||
|
||||
free(encoded);
|
||||
free(decoded);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base85_encode_decode_z_abbrev(void) {
|
||||
const ut8 zeros[4] = { 0, 0, 0, 0 };
|
||||
|
||||
char *enc = rz_base85_encode_dyn((const char *)zeros, 4, 0, 0, 0);
|
||||
mu_assert_notnull(enc, "rz_base85_encode_dyn returned NULL");
|
||||
mu_assert_streq(enc, "z", "encode zeros should be 'z'");
|
||||
|
||||
size_t decoded_len;
|
||||
char *decoded = rz_base85_decode_dyn("zz", -1, 0, 0, &decoded_len);
|
||||
mu_assert_notnull(decoded, "rz_base85_decode_dyn returned NULL");
|
||||
mu_assert_eq(decoded_len, 8U, "decoded length != 8");
|
||||
|
||||
for (size_t i = 0; i < decoded_len; i++) {
|
||||
mu_assert_eq(decoded[i], '\0', "decoded byte non‑zero");
|
||||
}
|
||||
|
||||
free(enc);
|
||||
free(decoded);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_rz_base85_decode_invalid_strict_vs_lenient(void) {
|
||||
const char bad[] = "FCfN8v";
|
||||
size_t decoded_len;
|
||||
|
||||
char *strict_out = rz_base85_decode_dyn(bad, -1, 0, 0, &decoded_len);
|
||||
mu_assert_true(strict_out == NULL, "decoder accepted garbage in strict mode");
|
||||
|
||||
char *lenient_out = rz_base85_decode_dyn(bad, -1, 0, 1, &decoded_len);
|
||||
mu_assert_notnull(lenient_out, "lenient decode returned NULL");
|
||||
mu_assert_eq(decoded_len, 4U, "decoded length != 4");
|
||||
mu_assert_true(memcmp(lenient_out, "test", 4) == 0, "lenient decode produced wrong bytes");
|
||||
|
||||
free(lenient_out);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
int all_tests(void) {
|
||||
mu_run_test(test_rz_base85_encode_dyn_nodelims);
|
||||
mu_run_test(test_rz_base85_decode_dyn_nodelims);
|
||||
mu_run_test(test_rz_base85_encode_dyn_delims);
|
||||
mu_run_test(test_rz_base85_decode_dyn_delims);
|
||||
mu_run_test(test_rz_base85_encode_decode_wrap10);
|
||||
mu_run_test(test_rz_base85_encode_decode_z_abbrev);
|
||||
mu_run_test(test_rz_base85_decode_invalid_strict_vs_lenient);
|
||||
|
||||
return tests_passed != tests_run;
|
||||
}
|
||||
|
||||
mu_main(all_tests)
|
||||
Loading…
Reference in a new issue