The script produces .vc and .vk files, and you decrypt again by running the vc script against the .vc file. No matter what the clear text file, both .vc and .vk are random (high entropy/cannot be compressed), such that the clear text remains hidden (frequency and/or shift xoring analysis wont reveal any detail).
Handy in that no passwords have to be remembered, when separated the content is secure, only by bringing both the .vc and .vk files back together again can the clear text content to revealed. Store one on a server, another on your laptop, and if either the laptop or server are cracked then the content is safe. Nor will faster/more powerful systems in the future be able to brute force the content either (future proof).
Note that both .vc and .vk are interchangeable, both are in effect the key to the other, so it doesn't matter which you might store on a server/elsewhere. Storing say the key file .vk on a remote ssh server, and storing the .vc locally, you might boot, sshfs mount the ssh server (where the .vk file(s) is (are) stored) and then drag/drop (or whatever) move the .vk file to be alongside (same folder) as the .vk file, and then open the content (run the vc script). Moving the .vk file will remove it off the server. Later you might encrypt the file again, and move the (newer) .vk file back to the server.
Code: Select all
#!/bin/ash
# Rufwoof Dec 2019
#
# Requires xor (C program binary)
if [ -z $1 ]; then
echo
echo "Usage : $0 <filename>"
echo
echo "If filename has a .vc suffix then decrypts"
echo
echo "One-Time-Pad (Vernam Cipher) style encryption"
echo
exit
fi
if [ ! -f $1 ]; then
if [ ! -L $1 ]; then
echo "File $1 not found"
exit
fi
fi
xor >/dev/null 2>&1
RC=$?
if [ $RC -ne 2 ]; then
echo "xor program as required by this script not found"
exit
fi
if [ -L $1 ]; then # if a sym link read filesize of actual
FILESIZE=$(stat -c%s "$(readlink -f $1)")
else
FILESIZE=$(stat -c%s "$1")
fi
filename=$(basename "$1")
ext="${filename##*.}"
if [ "$ext" = "vk" ]; then
B="${filename%.*}"
if [ -f ${B}.vc ]; then
if [ -L ${B}.vc ]; then
FS=$(stat -c%s "$(readlink -f ${B}.vc)")
else
FS=$(stat -c%s "${B}.vc")
fi
if [ $FILESIZE -eq $FS ]; then
echo "Hmm! Are you sure you want to encrypt a .vk file"
echo "Suspect you actually want to decrypt ${B}.vc"
C=1
while [ $C -ne 0 ]; do
echo -n "decrypt (d) or continue and encrypt (e) d/e : "
read -n1 ans; echo
case $ans in
d|D) filename="${B}.vc"; ext="vc"; C=0 ;;
e|E) C=0 ;;
*) echo "Answer should be d or e ... try again" ;;
esac
done
fi
fi
fi
if [ "$ext" = "vc" ]; then # decrypt
echo Decrypting
B="${filename%.*}"
if [ -f $B ]; then
echo -n "Output file $B already exists, overwrite y/n : "
read -n1 ans; echo
if [ "$ans" = "y" ] || [ $ans = "Y" ]; then
rm $B
else
echo exiting
exit
fi
fi
xor $B.vc $B.vk
RC=$?
if [ $RC -ne 0 ]; then
echo "Error occurred whilst running xor"
exit
fi
# if a sym link then we also remove the source file
if [ -L $B.vk ]; then
rm "$(readlink -f $B.vk)"
fi
rm $B.vk
if [ -L $B.vc ]; then
mv $(readlink -f ${B}.vc) ${B}
rm ${B}.vc
else
mv $B.vc $B
fi
else # encrypt
if [ -f ${filename}.vk ] || [ -f ${filename}.vc ]; then
echo "Output file(s) ${filename}.vc"
echo "and/or ${filename}.vk"
echo -n "already exist, overwrite y/n : "
read -n1 ans; echo
if [ "$ans" = "y" ] || [ $ans = "Y" ]; then
if [ -f ${filename}.vk ]; then
rm ${filename}.vk
fi
if [ -f ${filename}.vc ]; then
rm ${filename}.vc
fi
else
echo exiting
exit
fi
fi
echo "Generating high entropy random bytes keyfile"
dd if=/dev/random bs=32 iflag=fullblock | pv --bytes | head -c $FILESIZE >${filename}.vk
KEYFILESIZE=$(stat -c%s "${filename}.vk")
if [ $FILESIZE -gt $KEYFILESIZE ]; then # error in random file generation
echo "Mis-match between generated random key filesize and clear-text filesize"
echo "exiting"
rm ${filename}.vk
exit
fi
echo "Encrypting ..."
xor ${filename} ${filename}.vk
RC=$?
if [ $RC -ne 0 ]; then
echo "Error occurred whilst running xor"
exit
fi
if [ -L ${filename} ]; then
mv $(readlink -f ${filename}) ${filename}.vc
rm ${filename}
else
mv ${filename} ${filename}.vc
fi
fi
sync
Code: Select all
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <err.h>
/*
No error checking etc. intended to be called/run from
a wrapper shell script that does all the checks
Fast (buffers 32K blocks) XOR'ing of two files, where
the output file is the input file i.e. overwrites it
(saves on having to use a transitory file).
Rufwoof 2019
*/
int main(int argc, char *argv[]) {
char *in_n, *key_n, *out_n;
FILE *in_fp, *key_fp, *out_fp;
unsigned char data[32768], key[32768];
size_t in_l, key_l = 0, key_i = 0, data_i;
if (argc == 1) {
fprintf(stdout, "For usage see vc shell wrapper script\n");
return 2;
}
in_fp = fopen(argv[1],"rb");
key_fp = fopen(argv[2],"rb");
out_fp = fopen(argv[1],"rb+");
while (1)
{
data_i = 0;
in_l = fread(data, sizeof(unsigned char), sizeof(data), in_fp);
if (ferror(in_fp)) { return 1; }
if (in_l) {
while (data_i < in_l)
{
if (key_i >= key_l) {
key_i = 0;
key_l = fread(key,
sizeof(unsigned char),sizeof(key), key_fp);
if (ferror(key_fp)) { return 1; }
}
data[data_i] ^= key[key_i];data_i++;key_i++;
}
fwrite(data, sizeof(unsigned char), in_l, out_fp);
if (ferror(out_fp)) { return 1; }
}
else if (!in_l && feof(in_fp)) { return 0; }
/* End of in_fp, exit OK */
}
fclose(in_fp);
fclose(out_fp);
fclose(key_fp);
}
Fundamentally in Linux based systems the CSPRNG (Crytographically Secure Pseudo Random Number Generator) that derives its pool of random bytes from the likes of /dev/hwrng and the least significant bits of interrupts timestamps (hardware activity) sources both /dev/random and /dev/urandom. A difference however is that /dev/random will block if the kernels estimate of entropy is too low.
Large seemingly random sequences of numbers do not always have high entropy. You can take a small random number and turn it into a large random number and the entropy remains the same. For example, take a random number from 1 to 16 and compute its cryptographic hash with an algorithm like SHA-1 - the resulting 160 bit number looks very random, but it is one of only 16 possible such numbers. Guessing the number is just as easy as guessing a random number from 1 to 16. urandom will output low entropy (more predictable) data when the approximated entropy is low, and when removing (reading) long sequences of random bytes quickly entropy will tend to be low - and as such urandom random data tends to be significantly more predictable than /dev/random random data. One time pad (Vernam Cipher) requires that even if all possible valid ineligible clear text solutions are derived then the actual/correct solution remains obscure - cannot be differentiated. With /dev/urandom sourced random data that is much less assured than when using /dev/random random data.
It is important to use cryptographically secure random data as pure random could for instance yield all zeros as being a 'valid' random data sequence, that in turn would result in OTP yielding the same 'encrypted' output as the clear-text input. Our random data must exhibit the qualities of appearing random, and in the case of OTP the random data must also have relatively high entropy (otherwise even just cracking one/few bytes of clear text could highlight one overall most probable (or actual) solution.
A downside of using high entropy CSPRNG random data (/dev/random) is that it is much much slower to be produced compared to /dev/urandom. That is the price paid for higher security.