* 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
149 lines
4.5 KiB
C
149 lines
4.5 KiB
C
// 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;
|
||
}
|