#!/bin/bash umask 077 hash_dir=$(mktemp -d) function die() { echo "$*" >&2 exit 1 } if (( $# != 2));then die "usage: overonion e|d keyfile" fi mode=$1 if [[ "$mode" != e && "$mode" != d ]];then die "Use 'e' for encrypt or 'd' for decrypt" fi keyfile=$2 if [[ ! -e "$keyfile" ]];then die "Keyfile not found" fi if [[ ! -r "$keyfile" ]];then die "Cannot read keyfile" fi num_layers=$(wc -l < "$keyfile") if (( num_layers < 20 ));then die "Keyfile doesn't have enough layers to be an onion" fi hash_fields=$(awk '/^openssl-dgst / { print NF }' "$keyfile" | uniq ) if [[ "$mode" == e ]];then first_layer=$num_layers next_layer=-1 openssl_decrypt="" if [[ "$hash_fields" != 4 ]];then die "Refusing to encrypt with already-used key" fi else first_layer=1 next_layer=1 openssl_decrypt="-d" if [[ "$hash_fields" != 5 ]];then die "Key does not appear to have been used for encryption (it has no embedded hashes). Refusing to decrypt." fi fi function keyline() { awk -vline="$1" 'NR == line' "$keyfile" } function keyfield() { awk -vline="$1" -vfield="$2" 'NR == line { print $field }' "$keyfile" } function go() { layer=$1 if (( layer == 0 || layer > num_layers ));then cat else operation=$(keyfield "$layer" 1) if [[ "$operation" == openssl-enc ]];then openssl enc $openssl_decrypt "-$(keyfield "$layer" 2)" \ -nosalt -pass fd:37 37< <(keyfield "$layer" 3) elif [[ "$operation" == reverse ]];then "$(dirname "$0")/reverse" elif [[ "$operation" == openssl-dgst ]];then tee >(echo "$(keyline "$layer") $( { keyfield "$layer" 3 | base64 -d cat keyfield "$layer" 4 | base64 -d } | openssl dgst -binary "-$(keyfield "$layer" 2)" | base64 --wrap=0)" > "$hash_dir/$layer") else die "Unknown operation" fi | go $(( layer + next_layer )) fi } go "$first_layer" for hash_result in "$hash_dir"/*;do layer=$(basename "$hash_result") if [[ "$mode" == e ]];then # Add the hashes to keyfile key_aside_dir=$(mktemp -d "$keyfile.XXXXXXXXXX") key_aside="$key_aside_dir/key.orig" mv "$keyfile" "$key_aside" sed "${layer}s,.*,$(< "$hash_result")," "$key_aside" > "$keyfile" shred -u "$key_aside" rmdir "$key_aside_dir" else # Verify the hashes if [[ "$(awk '{ print $5 == $6 ? "hash ok" : "mismatch" }' "$hash_result")" != "hash ok" ]];then die "Hash check $layer failed" fi fi done rm -r "$hash_dir"