Skip to content

Instantly share code, notes, and snippets.

@loreb
Created December 17, 2014 20:19
Show Gist options
  • Save loreb/fb52824e296b2cfbd100 to your computer and use it in GitHub Desktop.
Save loreb/fb52824e296b2cfbd100 to your computer and use it in GitHub Desktop.
abomination: Spritz/RS14 in portable /bin/sh
#! /bin/sh
# "Spritz - a spongy RC4-like stream cipher and hash function"
# http://people.csail.mit.edu/rivest/pubs/RS14.bib.txt
# In case you're insane enough to use it, keep in mind that the registers
# (i,j,...) have NO PREFIX, so you'll have to add it(_spritz_i, ...).
# This script is meant to run on any POSIX shell with local variables
# ("local" is not included in POSIX. Idiots!) and POSIX commands (printf(1)).
# Bash,dash,mksh,zsh: see how fast they Absorb()/Drip() compared to vim...
# seq(1) is from plan9; linux/freebsd have it, openbsd does not.
seq() {
# zsh, seq 512: "job table full or recursion limit exceeded"
test $# -eq 1 && { seq 0 $1; return; }
test $# -eq 2 || { echo>&2 "seq!"; exit 100; }
test $1 -ge $2 || { echo $1; seq $((1 + $1)) $2; }
}
seq() {
test $# -eq 1 && { seq 0 $1; return; }
test $# -eq 2 || { echo>&2 "seq!"; exit 100; }
local n=$1
while [ $n -lt $2 ] ; do echo $n; n=$(($n + 1)) ; done
}
Swap() {
# S is implicit, params are the two indexes
local temp
eval "temp=\$S$1"
eval "S$1=\$S$2"
eval "S$2=$temp"
}
InitializeState() {
i=0
j=0
k=0
z=0
a=0
w=1
for v in $(seq 256) ; do
eval S"$v=$v"
done
}
Absorb() {
for v in "$@" ; do
AbsorbByte $v
done
}
AbsorbByte() {
AbsorbNibble $((0xF & $1))
AbsorbNibble $((0xF & ($1 >> 4) ))
}
AbsorbNibble() {
if [ $a -eq 128 ] ; then
Shuffle
fi
Swap $a $((128 + $1))
a=$(($a + 1))
}
AbsorbStop() {
if [ $a -eq 128 ] ; then
Shuffle
fi
a=$(($a + 1))
}
Shuffle() {
Whip 512
Crush
Whip 512
Crush
Whip 512
a=0
}
Whip() {
for v in $(seq $1) ; do
Update
done
w=$(($w + 2))
w=$(($w & 0xFF))
}
Crush() {
for v in $(seq 128) ; do
# if $[v] > S[N-1-v]
local N1v=$((256 - 1 - $v))
eval "test \$S$v -gt \$S$N1v" && Swap $v $N1v
done
}
# Squeeze() in /bin/sh is too ugly, even for me.
Drip() {
if [ $a -gt 0 ] ; then
Shuffle
fi
Update
# z = S[j + S[i + S[z + k]]]
local Szk
zk=$((0xFF & ($z+$k) ))
eval "Szk=\$S$zk"
local iS=$((0xFF & ($i + $Szk) ))
local Si
eval "Si=\$S$iS"
local jS=$((0xFF & ($j + $Si) ))
eval "z=\$S$jS"
return $z # POSIX says this is ok.
}
Update() {
i=$((0xFF & ($i + $w) ))
# j = 0xFF &(k + S[0xFF &(j + S[i])]);
eval "local jSi=\$(($j + \$S$i))" ; jSi=$((0xFF & $jSi))
eval "local SjSi=\$S$jSi"
j=$((0xFF & ($k + $SjSi) ))
# k = 0xFF &(i + k + S[j]);
eval "local Sj=\$S$j"
k=$((0xFF & ($i + $k + $Sj) ))
Swap $i $j
}
# "If the value of n is greater than 255, the results are undefined"
retcheck() {
return $1
}
for i in $(seq 256) ; do
retcheck $i
j=$?
test $i = $j&&continue
echo>&2 "canthappen: return $i yields $j..."
exit 123
done
if [ x"$NDEBUG" = x ] ; then
echo "Checking Spritz/RS14 test vectors..."
# Test stream cipher
InitializeState
Absorb 0x41 0x42 0x43 # "ABC"
for byte in q w e r t y u i ; do
Drip
printf %02x $?
done
echo
echo '77 9a 8e 01 f9 e9 cb c0 ... # streamcipher("ABC")'
# Test hash function
InitializeState
Absorb 0x41 0x42 0x43 # "ABC"
AbsorbStop
Absorb 32 # 256 bit => 32 bytes, as specified in the paper.
for byte in q w e r t y u i ; do
Drip
printf %02x $?
done
echo
echo '02 8f a2 b4 8b 93 4a 18 ... # hash("ABC")'
fi
# Reset ^^
InitializeState
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment