Skip to content

Instantly share code, notes, and snippets.

@markusfisch
Last active January 20, 2022 21:39
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save markusfisch/2648733 to your computer and use it in GitHub Desktop.
Save markusfisch/2648733 to your computer and use it in GitHub Desktop.
base64 fallback implementation in bash
#!/bin/bash
# Fallback base64 en-/decoder for systems that lack a native implementation
#
# @param ... - flags
which base64 &>/dev/null || {
# if even od is missing
which od &>/dev/null || od()
{
local C O=0 W=16
while IFS= read -r -d '' -n 1 C
do
(( O%W )) || printf '%07o' $O
printf ' %02x' "'$C"
(( ++O%W )) || echo
done
echo
}
if which awk &>/dev/null
then
base64()
{
# written by Danny Chouinard
# https://sites.google.com/site/dannychouinard/Home/unix-linux-trinkets/little-utilities/base64-and-base85-encoding-awk-scripts
awk \
'function encode64()
{
while( "od -v -t x1" | getline )
{
l = length( $0 );
for( c = 9; c <= l; ++c )
{
d = index( "0123456789abcdef", substr( $0, c, 1 ) );
if( d-- )
{
for( b = 1; b <= 4; ++b )
{
o = o*2+int( d/8 );
d = (d*2)%16;
if( ++obc == 6 )
{
printf substr( b64, ++o, 1 );
if( ++rc > 75 )
{
printf( "\n" );
rc = 0;
}
obc = 0;
o = 0;
}
}
}
}
}
if( obc )
{
while( obc++ < 6 )
{
o = o*2;
}
printf "%c", substr( b64, ++o, 1 );
}
print "==";
}
function decode64()
{
while( getline < "/dev/stdin" )
{
l = length( $0 );
for( i = 1; i <= l; ++i )
{
c = index( b64, substr( $0, i, 1 ) );
if( c-- )
{
for( b = 0; b < 6; ++b )
{
o = o*2+int( c/32 );
c = (c*2)%64;
if( ++obc == 8 )
{
printf "%c", o;
obc = 0;
o = 0;
}
}
}
}
}
}
BEGIN {
b64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
if( ARGV[1] == "-d" )
decode64();
else
encode64();
}' "$@"
}
else
cat <<EOF
WARNING: your system is missing base64 AND awk!
base64 encoding/decoding will be painfully slow!
EOF
base64()
{
local SET='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
[ "$1" == '-d' ] && {
local N=0 V=0 C S IFS=
while read -r -d '' -r -n1 C
do
[ "$C" == $'\n' ] && continue
if [ "$C" == '=' ]
then
V=$(( V << 6 ))
else
C=${SET#*$C}
C=$(( ${#SET}-${#C} ))
(( C )) || continue
V=$(( V << 6 | --C ))
fi
(( ++N == 4 )) && {
for (( S=16; S > -1; S -= 8 ))
do
C=$(( V >> S & 255 ))
# shellcheck disable=SC2059
printf "\\$(( C*100/64+C%64*10/8+C%8 ))"
done
V=0
N=0
}
done
return
}
od -v -t x1 | {
local V=0 W=0 SH=16 A S N L
while read -r -a A
do
for (( N=1, L=${#A[@]}; N < L; ++N ))
do
V=$(( 16#${A[$N]} << SH | V ))
(( (SH -= 8) < 0 )) || continue
for (( S=18; S > -1; S -= 6 ))
do
echo -n ${SET:$(( V >> S & 63 )):1}
(( ++W > 75 )) && {
echo
W=0
}
done
SH=16
V=0
done
done
if (( SH == 8 ))
then
N=11
elif (( SH == 0 ))
then
N=5
else
N=0
fi
(( N )) && {
for (( S=18; S > N; S -= 6 ))
do
echo -n ${SET:$(( V >> S & 63 )):1}
(( ++W > 75 )) && {
echo
W=0
}
done
for (( S=N/5; S--; ))
do
echo -n '='
done
}
echo
}
}
fi
}
base64 "$@"
@Shkeats
Copy link

Shkeats commented Mar 14, 2016

@markusfisch It would be great if there was a version of this without bash-isms that could run in older/simpler shells like Busybox Ash. Often in embedded environments (routers, IOT devices etc.) that have no base64 functions, bash isn't available either.

In my case dd-wrt router software lacks the base64 functions in it's old busybox version. I have a base64encoder shell script but not a decoder as yet. A modified version of your script would be perfect in these scenarios.

Edit
Found this shortly after writing:
https://github.com/mateusza/shellscripthttpd/blob/master/base64.sh

Copy link

ghost commented Aug 20, 2018

@markusfisch
Thanks for your sharing, in my recent disaster rescue case, I successfully apply your pure bash base64 decoder to transfer binary tool from survived ssh session. but your pure bash base64 decoder, has a bug when transforming decimal to octet in following line:
printf "\\$(( C*100/64+C%64*10/8+C%8 ))"
correct version should be:
printf "\\$(( C/64*100+C/8%8*10+C%8 ))"

BR
Grei

@zeronounours
Copy link

Thanks so much for sharing! I got some issues with od on non printable characters. I reworked the function, based on this answer on StackOverflow:

which od &>/dev/null || {
printf -v ascii \\%o {32..126}
printf -v ascii "$ascii"
printf -v cntrl %-20sE abtnvfr
od() {
    local char val offset=0 width=16
    while LANG=C IFS= read -r -d '' -n 1 char; do
        # print current offset at the beginning
        (( offset % width )) || printf '%07o' $offset
        # decode the current char value
        printf -v char "%q" "$char"
        case ${#char} in
            # printable char  "g" or "\["
            1|2 ) char=${ascii%$char*}; val=($((${#char}+32)));;
            # octal form "$'\002'"
            7 ) char=${char#*\'\\}; val=($((8#${char%\'})));;
            # control char "$'\n'"
            5 ) char=${char#*\'\\}; char=${cntrl%${char%\'}*}
            val=($((${#char}+7)));;
            # should no append
            * ) echo >&2 "od: ERROR: could no decode $char";;
        esac
        printf ' %02x' "$val"
        # end line after width chars
        (( ++offset % width )) || echo
    done
    echo
}
}

@benchonaut
Copy link

@markusfisch It would be great if there was a version of this without bash-isms that could run in older/simpler shells like Busybox Ash. Often in embedded environments (routers, IOT devices etc.) that have no base64 functions, bash isn't available either.

In my case dd-wrt router software lacks the base64 functions in it's old busybox version. I have a base64encoder shell script but not a decoder as yet. A modified version of your script would be perfect in these scenarios.

Edit
Found this shortly after writing:
https://github.com/mateusza/shellscripthttpd/blob/master/base64.sh

i tested the linked implementation and it failed with OOM , still i fixed the matesuza variant to support -w...

@markusfisch @Shkeats i also modified the above scripts to run with ash & awk (no od , no fold etc)
results are here : https://github.com/benchonaut/owrt-ash-b64-test-results

@stokito
Copy link

stokito commented Jan 20, 2022

I gathered some list of possible bas64 solutions https://gist.github.com/stokito/18ce554bd53bdb6355670066d5ae9943

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment