├── tests ├── docker │ ├── run_docker.sh │ ├── Dockerfile │ └── PKGBUILD ├── CMakeLists.txt ├── test-base64-decode.c ├── test-base64-encode.c ├── test-base32-encode.c └── test-base32-decode.c ├── baseencode.pc.in ├── .gitignore ├── SECURITY.md ├── .circleci └── config.yml ├── src ├── baseencode.h ├── common.h ├── base64.c └── base32.c ├── README.md ├── CMakeLists.txt ├── .github └── workflows │ └── codeql-analysis.yml └── LICENSE /tests/docker/run_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ -z "$1" ]]; then 4 | echo "Usage: $0 " 5 | exit 1 6 | fi 7 | 8 | docker build -t "testme:Dockerfile" --build-arg BRANCH="$1" . 9 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | IF(BUILD_TESTING) 3 | add_executable (test_all ../src/base32.c ../src/base64.c test-base32-encode.c test-base32-decode.c test-base64-encode.c test-base64-decode.c) 4 | 5 | target_link_libraries (test_all -lcriterion) 6 | 7 | add_test (NAME TestAll COMMAND test_all) 8 | ENDIF(BUILD_TESTING) 9 | -------------------------------------------------------------------------------- /baseencode.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | exec_prefix=${prefix} 3 | libdir=@PKGCONFIG_TARGET_LIBS@ 4 | includedir=@PKGCONFIG_TARGET_INCLUDES@ 5 | 6 | Name: libbaseencode 7 | Description: C library for encoding and decoding data using base32 or base64 8 | Version: @BUILD_VERSION@ 9 | URL: https://github.com/paolostivanin/libbaseencode 10 | Libs: -L${libdir} -lbaseencode 11 | Cflags: -I${includedir} 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | cmake-build-debug/ 3 | build/ 4 | 5 | # Object files 6 | *.o 7 | *.ko 8 | *.obj 9 | *.elf 10 | 11 | # Precompiled Headers 12 | *.gch 13 | *.pch 14 | 15 | # Libraries 16 | *.lib 17 | *.a 18 | *.la 19 | *.lo 20 | 21 | # Shared objects (inc. Windows DLLs) 22 | *.dll 23 | *.so 24 | *.so.* 25 | *.dylib 26 | 27 | # Executables 28 | *.exe 29 | *.out 30 | *.app 31 | *.i*86 32 | *.x86_64 33 | *.hex 34 | 35 | # Debug files 36 | *.dSYM/ 37 | *.su 38 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | The following list describes whether a version is eligible or not for security updates. 6 | 7 | | Version | Supported | EOL | 8 | | ------- | ------------------ |-------------| 9 | | 1.0.x | :heavy_check_mark: | 28-Feb-2022 | 10 | 11 | ## Reporting a Vulnerability 12 | 13 | Should you find a vulnerability, please report it privately to me via [e-mail](mailto:paolostivanin@users.noreply.github.com). 14 | The following is the workflow: 15 | - security issue is found, an e-mail is sent to me 16 | - within 24 hours I will reply to your e-mail with some info like, for example, whether it actually is a security issue and how serious it is 17 | - within 7 days I will develop and ship a fix 18 | - once the update is out I will open a [security advisory](https://github.com/paolostivanin/OTPClient/security/advisories) 19 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.0 2 | 3 | jobs: 4 | debian: 5 | docker: 6 | - image: debian:testing 7 | steps: 8 | - checkout 9 | - run: 10 | command: | 11 | apt update && apt -y install git gcc clang cmake libcriterion-dev 12 | mkdir build && cd "$_" 13 | cmake -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_TESTING=ON .. 14 | make && make install 15 | ./tests/test_all 16 | 17 | ubuntu: 18 | docker: 19 | - image: ubuntu:latest 20 | steps: 21 | - checkout 22 | - run: 23 | command: | 24 | apt update && apt -y install git gcc clang cmake libcriterion-dev 25 | mkdir build && cd "$_" 26 | cmake -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_TESTING=ON .. 27 | make && make install 28 | ./tests/test_all 29 | 30 | workflows: 31 | version: 2 32 | build: 33 | jobs: 34 | - debian 35 | - ubuntu 36 | -------------------------------------------------------------------------------- /tests/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM archlinux:latest 2 | 3 | ARG BRANCH=master 4 | 5 | COPY PKGBUILD /tmp/PKGBUILD 6 | 7 | RUN pacman -Syu --noconfirm ; \ 8 | pacman -S gcc git clang cmake pkg-config libgcrypt fakeroot sudo --noconfirm ; \ 9 | pacman -S base-devel --noconfirm 10 | 11 | RUN useradd -m -G wheel -s /bin/bash test ; \ 12 | cp /tmp/PKGBUILD /home/test/ && chown test:test /home/test/PKGBUILD ; \ 13 | sed -i 's/# %wheel ALL=(ALL) ALL/%wheel ALL=(ALL) NOPASSWD: ALL/' /etc/sudoers 14 | 15 | USER test 16 | RUN cd /home/test && makepkg 17 | 18 | USER root 19 | RUN pacman -U /home/test/*zst --noconfirm 20 | 21 | USER test 22 | RUN yay -S criterion --noconfirm 23 | 24 | USER root 25 | RUN git clone https://github.com/paolostivanin/libbaseencode -b $BRANCH ; \ 26 | cd libbaseencode ; \ 27 | mkdir build && cd $_ ; \ 28 | cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_TESTING=ON ; \ 29 | make -j2 ;\ 30 | ./tests/test_all ;\ 31 | make install 32 | 33 | -------------------------------------------------------------------------------- /src/baseencode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef enum _baseencode_errno { 4 | SUCCESS = 0, 5 | INVALID_INPUT = 1, 6 | EMPTY_STRING = 2, 7 | INPUT_TOO_BIG = 3, 8 | INVALID_B32_DATA = 4, 9 | INVALID_B64_DATA = 5, 10 | MEMORY_ALLOCATION = 6, 11 | } baseencode_error_t; 12 | 13 | 14 | char *base32_encode (const unsigned char *user_data, 15 | size_t data_len, 16 | baseencode_error_t *err); 17 | 18 | unsigned char *base32_decode (const char *user_data, 19 | size_t data_len, 20 | baseencode_error_t *err); 21 | 22 | char *base64_encode (const unsigned char *input_string, 23 | size_t input_length, 24 | baseencode_error_t *err); 25 | 26 | unsigned char *base64_decode (const char *input_string, 27 | size_t input_length, 28 | baseencode_error_t *err); -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "baseencode.h" 4 | 5 | #define BITS_PER_BYTE 8 6 | #define BITS_PER_B32_BLOCK 5 7 | #define BITS_PER_B64_BLOCK 6 8 | 9 | // 64 MB should be more than enough 10 | #define MAX_ENCODE_INPUT_LEN 64*1024*1024 11 | // if 64 MB of data is encoded than it should be also possible to decode it. That's why a bigger input is allowed for decoding 12 | #define MAX_DECODE_BASE32_INPUT_LEN ((MAX_ENCODE_INPUT_LEN * 8 + 4) / 5) 13 | #define MAX_DECODE_BASE64_INPUT_LEN ((MAX_ENCODE_INPUT_LEN * 8 + 4) / 6) 14 | 15 | 16 | static int 17 | strip_char(char *str, char strip) 18 | { 19 | int found = 0; 20 | char *p, *q; 21 | for (q = p = str; *p; p++) { 22 | if (*p != strip) { 23 | *q++ = *p; 24 | } else { 25 | found++; 26 | } 27 | } 28 | *q = '\0'; 29 | return found; 30 | } 31 | 32 | 33 | static void 34 | check_input(const unsigned char *user_data, size_t data_len, int max_len, baseencode_error_t *err) 35 | { 36 | if (user_data == NULL || (data_len == 0 && user_data[0] != '\0')) { 37 | *err = INVALID_INPUT; 38 | return; 39 | } else if (user_data[0] == '\0') { 40 | *err = EMPTY_STRING; 41 | return; 42 | } 43 | 44 | if (data_len > max_len) { 45 | *err = INPUT_TOO_BIG; 46 | return; 47 | } 48 | 49 | *err = SUCCESS; 50 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libbaseencode (deprecated since libcotp 2.0.0) 2 | 3 | Coverity Scan Build Status 5 | 6 | 7 | Library written in C for encoding and decoding data using base32 or base64 according to [RFC-4648](https://tools.ietf.org/html/rfc4648) 8 | 9 | ⚠️ libbasencode is deprecated since [libcotp](https://github.com/paolostivanin/libcotp) 2.0.0 has been released. ⚠️ 10 | 11 | # Requiremens 12 | - GCC or Clang 13 | - CMake 14 | 15 | # Build and Install 16 | ``` 17 | $ git clone https://github.com/paolostivanin/libbaseencode.git 18 | $ cd libbaseencode 19 | $ mkdir build && cd $_ 20 | $ cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr ../ 21 | $ make 22 | # make install 23 | ``` 24 | 25 | # How To Use It 26 | ``` 27 | char *b32_encoded = base32_encode(unsigned char *input, size_t input_length, baseencode_error_t *err); 28 | 29 | unsigned char *b32_decoded = base32_decode(char *input, size_t input_length, baseencode_error_t *err); 30 | 31 | char *b64_encoded = base64_encode(unsigned char *input, size_t input_length, baseencode_error_t *err); 32 | 33 | unsigned char *b64_decoded = base64_decode(char *input, size_t input_length, baseencode_error_t *err); 34 | ``` 35 | Please note that all the returned value **must be freed** once not needed any more. 36 | 37 | ## Errors 38 | In case of errors, `NULL` is returned and `err` is set to either one of: 39 | ``` 40 | INVALID_INPUT, EMPTY_STRING, INPUT_TOO_BIG, INVALID_B32_DATA, INVALID_B64_DATA, MEMORY_ALLOCATION, 41 | ``` 42 | otherwise, `err` is set to `SUCCESS` 43 | 44 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(baseencode) 3 | 4 | include(GNUInstallDirs) 5 | 6 | enable_testing() 7 | add_subdirectory(tests) 8 | 9 | set(BUILD_MAJOR "1") 10 | set(BUILD_MINOR "0") 11 | set(BUILD_VERSION "15") 12 | set(BUILD_VERSION ${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_VERSION}) 13 | 14 | set(CMAKE_C_STANDARD 11) 15 | 16 | set(BASEENCODE_HEADERS src/baseencode.h) 17 | set(SOURCE_FILES src/base32.c src/base64.c) 18 | 19 | set(CMAKE_C_FLAGS "-Wall -Werror -fPIC") 20 | 21 | add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES}) 22 | 23 | target_link_libraries(${PROJECT_NAME} ${PROJECT_LIBS}) 24 | 25 | set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${BUILD_VERSION} SOVERSION ${BUILD_MAJOR}) 26 | 27 | set(BASEENCODE_LIB_DIR "${CMAKE_INSTALL_LIBDIR}") 28 | set(BASEENCODE_INC_DIR "${CMAKE_INSTALL_INCLUDEDIR}") 29 | 30 | install( 31 | TARGETS ${PROJECT_NAME} 32 | ARCHIVE DESTINATION ${BASEENCODE_LIB_DIR} 33 | LIBRARY DESTINATION ${BASEENCODE_LIB_DIR} 34 | COMPONENT library 35 | ) 36 | 37 | install( 38 | FILES ${BASEENCODE_HEADERS} 39 | DESTINATION ${BASEENCODE_INC_DIR} 40 | ) 41 | 42 | # Allow adding prefix if CMAKE_INSTALL_INCLUDEDIR not absolute. 43 | if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") 44 | set(PKGCONFIG_TARGET_INCLUDES "${CMAKE_INSTALL_INCLUDEDIR}") 45 | else() 46 | set(PKGCONFIG_TARGET_INCLUDES "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") 47 | endif() 48 | # Allow adding prefix if CMAKE_INSTALL_LIBDIR not absolute. 49 | if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") 50 | set(PKGCONFIG_TARGET_LIBS "${CMAKE_INSTALL_LIBDIR}") 51 | else() 52 | set(PKGCONFIG_TARGET_LIBS "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") 53 | endif() 54 | 55 | configure_file("baseencode.pc.in" "baseencode.pc" @ONLY) 56 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/baseencode.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig/) 57 | -------------------------------------------------------------------------------- /tests/docker/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Jguer 2 | pkgname=yay-bin 3 | pkgver=11.0.2 4 | pkgrel=1 5 | pkgdesc="Yet another yogurt. Pacman wrapper and AUR helper written in go. Pre-compiled." 6 | arch=('x86_64' 'aarch64' 'armv6h' 'armv7h') 7 | url="https://github.com/Jguer/yay" 8 | license=('GPL3') 9 | depends=( 10 | 'git' 11 | ) 12 | optdepends=( 13 | 'sudo' 14 | ) 15 | provides=('yay') 16 | conflicts=('yay' 'libalpm.so<13') 17 | 18 | source_x86_64=("https://github.com/Jguer/yay/releases/download/v${pkgver}/${pkgname/-bin/}_${pkgver}_x86_64.tar.gz") 19 | source_aarch64=("https://github.com/Jguer/yay/releases/download/v${pkgver}/${pkgname/-bin/}_${pkgver}_aarch64.tar.gz") 20 | source_armv6h=("https://github.com/Jguer/yay/releases/download/v${pkgver}/${pkgname/-bin/}_${pkgver}_armv6h.tar.gz") 21 | source_armv7h=("https://github.com/Jguer/yay/releases/download/v${pkgver}/${pkgname/-bin/}_${pkgver}_armv7h.tar.gz") 22 | 23 | sha256sums_x86_64=('3b6334a4e719138c80f4c271a77b97bd740329677dfffe6bbe54679a0052140e') 24 | sha256sums_aarch64=('d1c1b2d74783cb366a86480fcea9d7fbb325d170815014642ac0790e9bad6637') 25 | sha256sums_armv6h=('7b4dd9d33d56b600955ead52bb5bd86bc9ac6fc45c24f0c6b686153b184eb905') 26 | sha256sums_armv7h=('80a6317383552272ca9a9d251531fb2ca6e310e811b034a680e6632d4a294f15') 27 | 28 | package() { 29 | _output="${srcdir}/${pkgname/-bin/}_${pkgver}_${CARCH}" 30 | install -Dm755 "${_output}/${pkgname/-bin/}" "${pkgdir}/usr/bin/${pkgname/-bin/}" 31 | install -Dm644 "${_output}/yay.8" "${pkgdir}/usr/share/man/man8/yay.8" 32 | 33 | # Shell autocompletion script 34 | install -Dm644 "${_output}/bash" "${pkgdir}/usr/share/bash-completion/completions/yay" 35 | install -Dm644 "${_output}/zsh" "${pkgdir}/usr/share/zsh/site-functions/_yay" 36 | install -Dm644 "${_output}/fish" "${pkgdir}/usr/share/fish/vendor_completions.d/yay.fish" 37 | 38 | LANGS="pt pt_BR en es eu fr_FR ja pl_PL ru_RU zh_CN ko" 39 | for lang in ${LANGS}; do 40 | install -Dm644 "${_output}/${lang}.mo" "${pkgdir}/usr/share/locale/${lang}/LC_MESSAGES/yay.mo" 41 | done 42 | } 43 | 44 | -------------------------------------------------------------------------------- /tests/test-base64-decode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../src/baseencode.h" 4 | 5 | 6 | Test(b64_decode_test, b64_all_chars) { 7 | baseencode_error_t err; 8 | const char *k = "QURGRzQxMyHCoyQlJiYoKC8/XsOnw6kqW10jKS0uLHw8Pis="; 9 | const char *k_dec = "ADFG413!£$%&&((/?^çé*[]#)-.,|<>+"; 10 | 11 | char *dk = base64_decode(k, strlen(k)+1, &err); 12 | 13 | cr_expect(strcmp(dk, k_dec) == 0, "Expected %s to be equal to %s", dk, k_dec); 14 | 15 | free(dk); 16 | } 17 | 18 | 19 | Test(b64_decode_test, b64_all_chars_noplusone) { 20 | baseencode_error_t err; 21 | const char *k = "QURGRzQxMyHCoyQlJiYoKC8/XsOnw6kqW10jKS0uLHw8Pis="; 22 | const char *k_dec = "ADFG413!£$%&&((/?^çé*[]#)-.,|<>+"; 23 | 24 | char *dk = base64_decode(k, strlen(k), &err); 25 | 26 | cr_expect(strcmp(dk, k_dec) == 0, "Expected %s to be equal to %s", dk, k_dec); 27 | 28 | free(dk); 29 | } 30 | 31 | 32 | 33 | Test(b64_decode_test, b64_rfc4648) { 34 | baseencode_error_t err; 35 | const char *k[] = {"", "Zg==" ,"Zm8=", "Zm9v", "Zm9vYg==" ,"Zm9vYmE=", "Zm9vYmFy"}; 36 | const char *k_dec[] = {"", "f", "fo", "foo", "foob", "fooba", "foobar"}; 37 | 38 | for (int i = 0; i < 7; i++) { 39 | char *dk = base64_decode(k[i], strlen(k[i])+1, &err); 40 | cr_expect(strcmp(dk, k_dec[i]) == 0, "Expected %s to be equal to %s", dk, k_dec[i]); 41 | free(dk); 42 | } 43 | } 44 | 45 | 46 | Test(b64_decode_test, b64_rfc4648_noplusone) { 47 | baseencode_error_t err; 48 | const char *k[] = {"", "Zg==" ,"Zm8=", "Zm9v", "Zm9vYg==" ,"Zm9vYmE=", "Zm9vYmFy"}; 49 | const char *k_dec[] = {"", "f", "fo", "foo", "foob", "fooba", "foobar"}; 50 | 51 | for (int i = 0; i < 7; i++) { 52 | char *dk = base64_decode(k[i], strlen(k[i]), &err); 53 | cr_expect(strcmp(dk, k_dec[i]) == 0, "Expected %s to be equal to %s", dk, k_dec[i]); 54 | free(dk); 55 | } 56 | } 57 | 58 | 59 | Test(b64_decode_test, b64_invalid_input) { 60 | baseencode_error_t err; 61 | const char *k = "£&/(&/"; 62 | size_t len = strlen(k); 63 | 64 | unsigned char *dk = base64_decode(k, len, &err); 65 | 66 | cr_expect_null(dk, "%s"); 67 | cr_expect_eq(err, INVALID_B64_DATA); 68 | } 69 | 70 | 71 | Test(b64_decode_test, b64_decode_input_exceeded) { 72 | baseencode_error_t err; 73 | const char *k = "ASDF"; 74 | size_t len = 128*1024*1024; 75 | 76 | unsigned char *dk = base64_decode(k, len, &err); 77 | 78 | cr_expect_null(dk, "%s"); 79 | cr_expect_eq(err, INPUT_TOO_BIG); 80 | } 81 | 82 | 83 | Test(b64_decode_test, b64_decode_input_whitespaces) { 84 | baseencode_error_t err; 85 | const char *k = "Zm 9v Y mFy"; 86 | const char *expected = "foobar"; 87 | 88 | unsigned char *dk = base64_decode(k, strlen(k), &err); 89 | 90 | cr_expect_str_eq(dk, expected, "%s"); 91 | } -------------------------------------------------------------------------------- /tests/test-base64-encode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../src/baseencode.h" 4 | 5 | 6 | Test(b64_encode_test, null_input) { 7 | baseencode_error_t err; 8 | const char *k = NULL; 9 | 10 | char *ek = base64_encode(k, 5, &err); 11 | 12 | cr_expect_null(ek, "%s"); 13 | } 14 | 15 | 16 | Test(b64_encode_test, data_nodata_size_nosize) { 17 | baseencode_error_t err; 18 | const char *k1 = ""; 19 | const char *k2 = "asdiasjdijis"; 20 | 21 | // test no-data with given size, data with no-size and no-data no-size 22 | char *ek1 = base64_encode(k1, 30, &err); 23 | char *ek2 = base64_encode(k2, 0, &err); 24 | 25 | cr_expect(strcmp(k1, ek1) == 0, "Expected %s to be equal to %s", ek1, k1); 26 | cr_expect_null(ek2, "%s"); 27 | 28 | free(ek1); 29 | } 30 | 31 | 32 | Test(b64_encode_test, b64_all_chars) { 33 | baseencode_error_t err; 34 | const char *k = "ADFG413!£$%&&((/?^çé*[]#)-.,|<>+"; 35 | const char *k_enc = "QURGRzQxMyHCoyQlJiYoKC8/XsOnw6kqW10jKS0uLHw8Pis="; 36 | 37 | char *ek = base64_encode(k, strlen(k)+1, &err); 38 | 39 | cr_expect(strcmp(ek, k_enc) == 0, "Expected %s to be equal to %s", ek, k_enc); 40 | 41 | free(ek); 42 | } 43 | 44 | 45 | Test(b64_encode_test, b64_all_chars_noplusone) { 46 | baseencode_error_t err; 47 | const char *k = "ADFG413!£$%&&((/?^çé*[]#)-.,|<>+"; 48 | const char *k_enc = "QURGRzQxMyHCoyQlJiYoKC8/XsOnw6kqW10jKS0uLHw8Pis="; 49 | 50 | char *ek = base64_encode(k, strlen(k), &err); 51 | 52 | cr_expect(strcmp(ek, k_enc) == 0, "Expected %s to be equal to %s", ek, k_enc); 53 | 54 | free(ek); 55 | } 56 | 57 | 58 | Test(b64_encode_test, b64_rfc4648) { 59 | baseencode_error_t err; 60 | const char *k[] = {"", "f", "fo", "foo", "foob", "fooba", "foobar"}; 61 | const char *k_enc[] = {"", "Zg==" ,"Zm8=", "Zm9v", "Zm9vYg==" ,"Zm9vYmE=", "Zm9vYmFy"}; 62 | 63 | for (int i = 0; i < 7; i++) { 64 | char *ek = base64_encode(k[i], strlen(k[i])+1, &err); 65 | cr_expect(strcmp(ek, k_enc[i]) == 0, "Expected %s to be equal to %s", ek, k_enc[i]); 66 | free(ek); 67 | } 68 | } 69 | 70 | 71 | Test(b64_encode_test, b64_rfc4648_noplusone) { 72 | baseencode_error_t err; 73 | const char *k[] = {"", "f", "fo", "foo", "foob", "fooba", "foobar"}; 74 | const char *k_enc[] = {"", "Zg==" ,"Zm8=", "Zm9v", "Zm9vYg==" ,"Zm9vYmE=", "Zm9vYmFy"}; 75 | 76 | for (int i = 0; i < 7; i++) { 77 | char *ek = base64_encode(k[i], strlen(k[i]), &err); 78 | cr_expect(strcmp(ek, k_enc[i]) == 0, "Expected %s to be equal to %s", ek, k_enc[i]); 79 | free(ek); 80 | } 81 | } 82 | 83 | 84 | Test(b64_encode_test, b64_encode_input_exceeded) { 85 | baseencode_error_t err; 86 | const char *k = "test"; 87 | size_t len = 65*1024*1024; 88 | 89 | char *ek = base64_encode(k, len, &err); 90 | cr_expect_null(ek, "%s"); 91 | cr_expect_eq(err, INPUT_TOO_BIG); 92 | } -------------------------------------------------------------------------------- /tests/test-base32-encode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../src/baseencode.h" 4 | 5 | 6 | Test(b32_encode_test, null_input) { 7 | baseencode_error_t err; 8 | const char *k = NULL; 9 | 10 | char *ek = base32_encode(k, 5, &err); 11 | 12 | cr_expect_null(ek, "%s"); 13 | } 14 | 15 | 16 | Test(b32_encode_test, data_nodata_size_nosize) { 17 | baseencode_error_t err; 18 | const char *k1 = ""; 19 | const char *k2 = "asdiasjdijis"; 20 | 21 | // test no-data with given size, data with no-size and no-data no-size 22 | char *ek1 = base32_encode(k1, 30, &err); 23 | char *ek2 = base32_encode(k2, 0, &err); 24 | 25 | cr_expect(strcmp(k1, ek1) == 0, "Expected %s to be equal to %s", ek1, k1); 26 | cr_expect_null(ek2, "%s"); 27 | 28 | free(ek1); 29 | } 30 | 31 | 32 | Test(b32_encode_test, b32_all_chars) { 33 | baseencode_error_t err; 34 | const char *k = "ADFG413!£$%&&((/?^çé*[]#)-.,|<>+"; 35 | const char *k_enc = "IFCEMRZUGEZSDQVDEQSSMJRIFAXT6XWDU7B2SKS3LURSSLJOFR6DYPRL"; 36 | 37 | char *ek = base32_encode(k, strlen(k)+1, &err); 38 | 39 | cr_expect(strcmp(ek, k_enc) == 0, "Expected %s to be equal to %s", ek, k_enc); 40 | 41 | free(ek); 42 | } 43 | 44 | 45 | Test(b32_encode_test, b32_all_chars_noplusone) { 46 | baseencode_error_t err; 47 | const char *k = "ADFG413!£$%&&((/?^çé*[]#)-.,|<>+"; 48 | const char *k_enc = "IFCEMRZUGEZSDQVDEQSSMJRIFAXT6XWDU7B2SKS3LURSSLJOFR6DYPRL"; 49 | 50 | char *ek = base32_encode(k, strlen(k), &err); 51 | 52 | cr_expect(strcmp(ek, k_enc) == 0, "Expected %s to be equal to %s", ek, k_enc); 53 | 54 | free(ek); 55 | } 56 | 57 | 58 | Test(b32_encode_test, b32_rfc4648) { 59 | baseencode_error_t err; 60 | const char *k[] = {"", "f", "fo", "foo", "foob", "fooba", "foobar"}; 61 | const char *k_enc[] = {"", "MY======", "MZXQ====", "MZXW6===", "MZXW6YQ=", "MZXW6YTB", "MZXW6YTBOI======"}; 62 | 63 | for (int i = 0; i < 7; i++) { 64 | char *ek = base32_encode(k[i], strlen(k[i])+1, &err); 65 | cr_expect(strcmp(ek, k_enc[i]) == 0, "Expected %s to be equal to %s", ek, k_enc[i]); 66 | free(ek); 67 | } 68 | } 69 | 70 | 71 | Test(b32_encode_test, b32_rfc4648_noplusone) { 72 | baseencode_error_t err; 73 | const char *k[] = {"", "f", "fo", "foo", "foob", "fooba", "foobar"}; 74 | const char *k_enc[] = {"", "MY======", "MZXQ====", "MZXW6===", "MZXW6YQ=", "MZXW6YTB", "MZXW6YTBOI======"}; 75 | 76 | for (int i = 0; i < 7; i++) { 77 | char *ek = base32_encode(k[i], strlen(k[i]), &err); 78 | cr_expect(strcmp(ek, k_enc[i]) == 0, "Expected %s to be equal to %s", ek, k_enc[i]); 79 | free(ek); 80 | } 81 | } 82 | 83 | 84 | Test(b32_encode_test, b32_encode_input_exceeded) { 85 | baseencode_error_t err; 86 | const char *k = "test"; 87 | size_t len = 65*1024*1024; 88 | 89 | char *ek = base32_encode(k, len, &err); 90 | cr_expect_null(ek, "%s"); 91 | cr_expect_eq(err, INPUT_TOO_BIG); 92 | } -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '21 3 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'cpp' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v3 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | 52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 53 | # queries: security-extended,security-and-quality 54 | 55 | 56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 57 | # If this step fails, then you should remove it and run the build manually (see below) 58 | - name: Autobuild 59 | uses: github/codeql-action/autobuild@v2 60 | 61 | # ℹ️ Command-line programs to run using the OS shell. 62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 63 | 64 | # If the Autobuild fails above, remove it and uncomment the following three lines. 65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 66 | 67 | # - run: | 68 | # echo "Run, Build Application using script" 69 | # ./location_of_script_within_repo/buildscript.sh 70 | 71 | - name: Perform CodeQL Analysis 72 | uses: github/codeql-action/analyze@v2 73 | -------------------------------------------------------------------------------- /tests/test-base32-decode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../src/baseencode.h" 4 | 5 | 6 | Test(b32_decode_test, b32_all_chars) { 7 | baseencode_error_t err; 8 | const char *k = "IFCEMRZUGEZSDQVDEQSSMJRIFAXT6XWDU7B2SKS3LURSSLJOFR6DYPRL"; 9 | const char *k_dec = "ADFG413!£$%&&((/?^çé*[]#)-.,|<>+"; 10 | 11 | char *dk = base32_decode(k, strlen(k)+1, &err); 12 | 13 | cr_expect(strcmp(dk, k_dec) == 0, "Expected %s to be equal to %s", dk, k_dec); 14 | 15 | free(dk); 16 | } 17 | 18 | 19 | Test(b32_decode_test, b32_all_chars_noplusone) { 20 | baseencode_error_t err; 21 | const char *k = "IFCEMRZUGEZSDQVDEQSSMJRIFAXT6XWDU7B2SKS3LURSSLJOFR6DYPRL"; 22 | const char *k_dec = "ADFG413!£$%&&((/?^çé*[]#)-.,|<>+"; 23 | 24 | char *dk = base32_decode(k, strlen(k), &err); 25 | 26 | cr_expect(strcmp(dk, k_dec) == 0, "Expected %s to be equal to %s", dk, k_dec); 27 | 28 | free(dk); 29 | } 30 | 31 | 32 | Test(b32_decode_test, b32_rfc4648) { 33 | baseencode_error_t err; 34 | const char *k[] = {"", "MY======", "MZXQ====", "MZXW6===", "MZXW6YQ=", "MZXW6YTB", "MZXW6YTBOI======"}; 35 | const char *k_dec[] = {"", "f", "fo", "foo", "foob", "fooba", "foobar"}; 36 | 37 | for (int i = 0; i < 7; i++) { 38 | char *dk = base32_decode(k[i], strlen(k[i])+1, &err); 39 | cr_expect(strcmp(dk, k_dec[i]) == 0, "Expected %s to be equal to %s", dk, k_dec[i]); 40 | free(dk); 41 | } 42 | } 43 | 44 | 45 | Test(b32_decode_test, b32_rfc4648_noplusone) { 46 | baseencode_error_t err; 47 | const char *k[] = {"", "MY======", "MZXQ====", "MZXW6===", "MZXW6YQ=", "MZXW6YTB", "MZXW6YTBOI======"}; 48 | const char *k_dec[] = {"", "f", "fo", "foo", "foob", "fooba", "foobar"}; 49 | 50 | for (int i = 0; i < 7; i++) { 51 | char *dk = base32_decode(k[i], strlen(k[i]), &err); 52 | cr_expect(strcmp(dk, k_dec[i]) == 0, "Expected %s to be equal to %s", dk, k_dec[i]); 53 | free(dk); 54 | } 55 | } 56 | 57 | 58 | Test(b32_decode_test, b32_invalid_input) { 59 | baseencode_error_t err; 60 | const char *k = "£&/(&/"; 61 | size_t len = strlen(k); 62 | 63 | unsigned char *dk = base32_decode(k, len, &err); 64 | 65 | cr_expect_null(dk, "%s"); 66 | cr_expect_eq(err, INVALID_B32_DATA); 67 | } 68 | 69 | 70 | Test(b32_decode_test, b32_decode_input_exceeded) { 71 | baseencode_error_t err; 72 | const char *k = "ASDF"; 73 | size_t len = 128*1024*1024; 74 | 75 | unsigned char *dk = base32_decode(k, len, &err); 76 | 77 | cr_expect_null(dk, "%s"); 78 | cr_expect_eq(err, INPUT_TOO_BIG); 79 | } 80 | 81 | 82 | Test(b32_decode_test, b32_decode_input_whitespaces) { 83 | baseencode_error_t err; 84 | const char *k = "MZ XW 6Y TB"; 85 | const char *expected = "fooba"; 86 | 87 | unsigned char *dk = base32_decode(k, strlen(k), &err); 88 | 89 | cr_expect_str_eq(dk, expected, "%s"); 90 | } 91 | 92 | Test(b32_decode_test, b32_decode_encode_null) { 93 | const char* token = "LLFTSZYMUGKHEDQBAAACAZAMUFKKVFLS"; 94 | baseencode_error_t err; 95 | 96 | unsigned char* binary = base32_decode(token, strlen(token)+1, &err); 97 | cr_expect_eq(err, SUCCESS); 98 | 99 | char* result = base32_encode(binary, 20, &err); 100 | cr_expect_eq(err, SUCCESS); 101 | 102 | cr_expect_str_eq(result, token, "%s"); 103 | } 104 | 105 | -------------------------------------------------------------------------------- /src/base64.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "common.h" 7 | 8 | static int is_valid_b64_input(const char *user_data, size_t data_len); 9 | 10 | static int get_char_index(unsigned char c); 11 | 12 | static const unsigned char b64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 13 | 14 | 15 | char * 16 | base64_encode(const unsigned char *user_data, size_t data_len, baseencode_error_t *err) 17 | { 18 | baseencode_error_t error; 19 | check_input(user_data, data_len, MAX_ENCODE_INPUT_LEN, &error); 20 | if (error != SUCCESS) { 21 | *err = error; 22 | if (error == EMPTY_STRING) { 23 | return strdup(""); 24 | } else { 25 | return NULL; 26 | } 27 | } 28 | 29 | size_t user_data_chars = 0, total_bits = 0; 30 | int num_of_equals = 0; 31 | for (int i = 0; i < data_len; i++) { 32 | // As it's not known whether data_len is with or without the +1 for the null byte, a manual check is required. 33 | if (user_data[i] != '\0') { 34 | total_bits += 8; 35 | user_data_chars += 1; 36 | } else { 37 | break; 38 | } 39 | } 40 | switch (total_bits % 24) { 41 | case 8: 42 | num_of_equals = 2; 43 | break; 44 | case 16: 45 | num_of_equals = 1; 46 | break; 47 | default: 48 | break; 49 | } 50 | 51 | size_t output_length = (user_data_chars * 8 + 4) / 6; 52 | char *encoded_data = calloc(output_length + num_of_equals + 1 + 3, 1); 53 | if (encoded_data == NULL) { 54 | *err = MEMORY_ALLOCATION; 55 | return NULL; 56 | } 57 | 58 | uint8_t first_octet, second_octet, third_octet; 59 | for (int i = 0, j = 0, triple = 0; i < user_data_chars + 1;) { 60 | first_octet = (uint8_t) (i < user_data_chars+1 ? user_data[i++] : 0); 61 | second_octet = (uint8_t) (i < user_data_chars+1 ? user_data[i++] : 0); 62 | third_octet = (uint8_t) (i < user_data_chars+1 ? user_data[i++] : 0); 63 | triple = (first_octet << 0x10) + (second_octet << 0x08) + third_octet; 64 | 65 | encoded_data[j++] = b64_alphabet[(triple >> 0x12) & 0x3F]; 66 | encoded_data[j++] = b64_alphabet[(triple >> 0x0C) & 0x3F]; 67 | encoded_data[j++] = b64_alphabet[(triple >> 0x06) & 0x3F]; 68 | encoded_data[j++] = b64_alphabet[(triple >> 0x00) & 0x3F]; 69 | } 70 | 71 | for (int i = 0; i < num_of_equals; i++) { 72 | encoded_data[output_length + i] = '='; 73 | } 74 | encoded_data[output_length + num_of_equals] = '\0'; 75 | 76 | *err = SUCCESS; 77 | return encoded_data; 78 | } 79 | 80 | 81 | unsigned char * 82 | base64_decode(const char *user_data_untrimmed, size_t data_len, baseencode_error_t *err) 83 | { 84 | baseencode_error_t error; 85 | check_input((unsigned char *)user_data_untrimmed, data_len, MAX_DECODE_BASE64_INPUT_LEN, &error); 86 | if (error != SUCCESS) { 87 | *err = error; 88 | if (error == EMPTY_STRING) { 89 | return (unsigned char *) strdup(""); 90 | } else { 91 | return NULL; 92 | } 93 | } 94 | 95 | char *user_data = strdup(user_data_untrimmed); 96 | if (user_data == NULL) { 97 | *err = MEMORY_ALLOCATION; 98 | return NULL; 99 | } 100 | data_len -= strip_char(user_data, ' '); 101 | 102 | if (!is_valid_b64_input(user_data, data_len)) { 103 | *err = INVALID_B64_DATA; 104 | free(user_data); 105 | return NULL; 106 | } 107 | 108 | size_t user_data_chars = 0; 109 | for (int z = 0; z < data_len; z++) { 110 | // As it's not known whether data_len is with or without the +1 for the null byte, a manual check is required. 111 | if (user_data[z] != '=' && user_data[z] != '\0') { 112 | user_data_chars += 1; 113 | } 114 | } 115 | 116 | size_t output_length = data_len / 4 * 3; 117 | unsigned char *decoded_data = calloc(output_length + 1, 1); 118 | if (decoded_data == NULL) { 119 | *err = MEMORY_ALLOCATION; 120 | free(user_data); 121 | return NULL; 122 | } 123 | 124 | uint8_t mask = 0, current_byte = 0; 125 | int bits_left = 8; 126 | for (int i = 0, j = 0; i < user_data_chars; i++) { 127 | int char_index = get_char_index((unsigned char)user_data[i]); 128 | if (bits_left > BITS_PER_B64_BLOCK) { 129 | mask = (uint8_t) char_index << (bits_left - BITS_PER_B64_BLOCK); 130 | current_byte = (uint8_t) (current_byte | mask); 131 | bits_left -= BITS_PER_B64_BLOCK; 132 | } else { 133 | mask = (uint8_t) char_index >> (BITS_PER_B64_BLOCK - bits_left); 134 | current_byte = (uint8_t) (current_byte | mask); 135 | decoded_data[j++] = current_byte; 136 | current_byte = (uint8_t) (char_index << (BITS_PER_BYTE - BITS_PER_B64_BLOCK + bits_left)); 137 | bits_left += BITS_PER_BYTE - BITS_PER_B64_BLOCK; 138 | } 139 | } 140 | decoded_data[output_length] = '\0'; 141 | 142 | free(user_data); 143 | 144 | *err = SUCCESS; 145 | return decoded_data; 146 | } 147 | 148 | 149 | static int 150 | is_valid_b64_input(const char *user_data, size_t data_len) 151 | { 152 | size_t found = 0, b64_alphabet_len = sizeof(b64_alphabet); 153 | for (int i = 0; i < data_len; i++) { 154 | for(int j = 0; j < b64_alphabet_len; j++) { 155 | if(user_data[i] == b64_alphabet[j] || user_data[i] == '=') { 156 | found++; 157 | break; 158 | } 159 | } 160 | } 161 | if (found != data_len) { 162 | return 0; 163 | } else { 164 | return 1; 165 | } 166 | } 167 | 168 | 169 | static int 170 | get_char_index(unsigned char c) 171 | { 172 | for (int i = 0; i < sizeof(b64_alphabet); i++) { 173 | if (b64_alphabet[i] == c) { 174 | return i; 175 | } 176 | } 177 | return -1; 178 | } 179 | -------------------------------------------------------------------------------- /src/base32.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "common.h" 6 | 7 | 8 | static int is_valid_b32_input(const char *user_data, size_t data_len); 9 | 10 | static int get_char_index(unsigned char c); 11 | 12 | static const unsigned char b32_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; 13 | 14 | 15 | // The encoding process represents 40-bit groups of input bits as output strings of 8 encoded characters. The input data must be null terminated. 16 | char * 17 | base32_encode(const unsigned char *user_data, size_t data_len, baseencode_error_t *err) 18 | { 19 | baseencode_error_t error; 20 | check_input(user_data, data_len, MAX_ENCODE_INPUT_LEN, &error); 21 | if (error != SUCCESS) { 22 | *err = error; 23 | if (error == EMPTY_STRING) { 24 | return strdup(""); 25 | } else { 26 | return NULL; 27 | } 28 | } 29 | 30 | size_t user_data_chars = 0, total_bits = 0; 31 | int num_of_equals = 0; 32 | for (int i = 0; i < data_len; i++) { 33 | // As it's not known whether data_len is with or without the +1 for the null byte, a manual check is required. 34 | // Check for null byte only at the end of the user given length, otherwise issue#23 may occur 35 | if (user_data[i] == '\0' && i == data_len-1) { 36 | break; 37 | } else { 38 | total_bits += 8; 39 | user_data_chars += 1; 40 | } 41 | } 42 | switch (total_bits % 40) { 43 | case 8: 44 | num_of_equals = 6; 45 | break; 46 | case 16: 47 | num_of_equals = 4; 48 | break; 49 | case 24: 50 | num_of_equals = 3; 51 | break; 52 | case 32: 53 | num_of_equals = 1; 54 | break; 55 | default: 56 | break; 57 | } 58 | 59 | size_t output_length = (user_data_chars * 8 + 4) / 5; 60 | char *encoded_data = calloc(output_length + num_of_equals + 1, 1); 61 | if (encoded_data == NULL) { 62 | *err = MEMORY_ALLOCATION; 63 | return NULL; 64 | } 65 | 66 | uint64_t first_octet, second_octet, third_octet, fourth_octet, fifth_octet; 67 | uint64_t quintuple; 68 | for (int i = 0, j = 0; i < user_data_chars;) { 69 | first_octet = i < user_data_chars ? user_data[i++] : 0; 70 | second_octet = i < user_data_chars ? user_data[i++] : 0; 71 | third_octet = i < user_data_chars ? user_data[i++] : 0; 72 | fourth_octet = i < user_data_chars ? user_data[i++] : 0; 73 | fifth_octet = i < user_data_chars ? user_data[i++] : 0; 74 | quintuple = 75 | ((first_octet >> 3) << 35) + 76 | ((((first_octet & 0x7) << 2) | (second_octet >> 6)) << 30) + 77 | (((second_octet & 0x3F) >> 1) << 25) + 78 | ((((second_octet & 0x01) << 4) | (third_octet >> 4)) << 20) + 79 | ((((third_octet & 0xF) << 1) | (fourth_octet >> 7)) << 15) + 80 | (((fourth_octet & 0x7F) >> 2) << 10) + 81 | ((((fourth_octet & 0x3) << 3) | (fifth_octet >> 5)) << 5) + 82 | (fifth_octet & 0x1F); 83 | 84 | encoded_data[j++] = b32_alphabet[(quintuple >> 35) & 0x1F]; 85 | encoded_data[j++] = b32_alphabet[(quintuple >> 30) & 0x1F]; 86 | encoded_data[j++] = b32_alphabet[(quintuple >> 25) & 0x1F]; 87 | encoded_data[j++] = b32_alphabet[(quintuple >> 20) & 0x1F]; 88 | encoded_data[j++] = b32_alphabet[(quintuple >> 15) & 0x1F]; 89 | encoded_data[j++] = b32_alphabet[(quintuple >> 10) & 0x1F]; 90 | encoded_data[j++] = b32_alphabet[(quintuple >> 5) & 0x1F]; 91 | encoded_data[j++] = b32_alphabet[(quintuple >> 0) & 0x1F]; 92 | } 93 | 94 | for (int i = 0; i < num_of_equals; i++) { 95 | encoded_data[output_length + i] = '='; 96 | } 97 | encoded_data[output_length + num_of_equals] = '\0'; 98 | 99 | *err = SUCCESS; 100 | return encoded_data; 101 | } 102 | 103 | 104 | unsigned char * 105 | base32_decode(const char *user_data_untrimmed, size_t data_len, baseencode_error_t *err) 106 | { 107 | baseencode_error_t error; 108 | check_input((unsigned char *)user_data_untrimmed, data_len, MAX_DECODE_BASE32_INPUT_LEN, &error); 109 | if (error != SUCCESS) { 110 | *err = error; 111 | if (error == EMPTY_STRING) { 112 | return (unsigned char *) strdup(""); 113 | } else { 114 | return NULL; 115 | } 116 | } 117 | 118 | char *user_data = strdup(user_data_untrimmed); 119 | if (user_data == NULL) { 120 | *err = MEMORY_ALLOCATION; 121 | return NULL; 122 | } 123 | data_len -= strip_char(user_data, ' '); 124 | 125 | if (!is_valid_b32_input(user_data, data_len)) { 126 | *err = INVALID_B32_DATA; 127 | free(user_data); 128 | return NULL; 129 | } 130 | 131 | size_t user_data_chars = 0; 132 | for (int i = 0; i < data_len; i++) { 133 | // As it's not known whether data_len is with or without the +1 for the null byte, a manual check is required. 134 | if (user_data[i] != '=' && user_data[i] != '\0') { 135 | user_data_chars += 1; 136 | } 137 | } 138 | 139 | size_t output_length = (size_t) ((user_data_chars + 1.6 + 1) / 1.6); // round up 140 | unsigned char *decoded_data = calloc(output_length + 1, 1); 141 | if (decoded_data == NULL) { 142 | *err = MEMORY_ALLOCATION; 143 | free(user_data); 144 | return NULL; 145 | } 146 | 147 | uint8_t mask = 0, current_byte = 0; 148 | int bits_left = 8; 149 | for (int i = 0, j = 0; i < user_data_chars; i++) { 150 | int char_index = get_char_index((unsigned char)user_data[i]); 151 | if (bits_left > BITS_PER_B32_BLOCK) { 152 | mask = (uint8_t) char_index << (bits_left - BITS_PER_B32_BLOCK); 153 | current_byte = (uint8_t) (current_byte | mask); 154 | bits_left -= BITS_PER_B32_BLOCK; 155 | } else { 156 | mask = (uint8_t) char_index >> (BITS_PER_B32_BLOCK - bits_left); 157 | current_byte = (uint8_t) (current_byte | mask); 158 | decoded_data[j++] = current_byte; 159 | current_byte = (uint8_t) (char_index << (BITS_PER_BYTE - BITS_PER_B32_BLOCK + bits_left)); 160 | bits_left += BITS_PER_BYTE - BITS_PER_B32_BLOCK; 161 | } 162 | } 163 | decoded_data[output_length] = '\0'; 164 | 165 | free(user_data); 166 | 167 | *err = SUCCESS; 168 | return decoded_data; 169 | } 170 | 171 | 172 | static int 173 | is_valid_b32_input(const char *user_data, size_t data_len) 174 | { 175 | size_t found = 0, b32_alphabet_len = sizeof(b32_alphabet); 176 | for (int i = 0; i < data_len; i++) { 177 | if (user_data[i] == '\0') { 178 | found++; 179 | break; 180 | } 181 | for(int j = 0; j < b32_alphabet_len; j++) { 182 | if(user_data[i] == b32_alphabet[j] || user_data[i] == '=') { 183 | found++; 184 | break; 185 | } 186 | } 187 | } 188 | if (found != data_len) { 189 | return 0; 190 | } else { 191 | return 1; 192 | } 193 | } 194 | 195 | 196 | static int 197 | get_char_index(unsigned char c) 198 | { 199 | for (int i = 0; i < sizeof(b32_alphabet); i++) { 200 | if (b32_alphabet[i] == c) { 201 | return i; 202 | } 203 | } 204 | return -1; 205 | } 206 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 Paolo Stivanin 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------