X-Git-Url: https://zdv2.bktei.com/gitweb/BK-2020-03.git/blobdiff_plain/dd9f30387a6f3e81b56ea3e86bef174422b37c77..fdf917e1ee70b612202fe10fab2e73d0ea077017:/unitproc/bkpoe diff --git a/unitproc/bkpoe b/unitproc/bkpoe new file mode 100755 index 0000000..ff4c738 --- /dev/null +++ b/unitproc/bkpoe @@ -0,0 +1,678 @@ +#!/bin/bash +# +# Date: 2020-02-18T20:06Z +# +# Author: Steven Baltakatei Sandoval (baltakatei.com) +# +# License: This bash script, 'bkpoe', is licensed under GPLv3 or +# later by Steven Baltakatei Sandoval: +# +# 'bkpoe', a file system proof of existence generator +# Copyright (C) 2020 Steven Baltakatei Sandoval (baltakatei.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# A copy of the GNU General Public License may be found at +# . +# +# Description: This is a simple script that uses GNU Coreutils +# OpenTimestamps to generate a proof that permits a user to prove the +# existence of any individual file within a directory. A proof +# consists of: +# +# - An OpenTimestamps 'ots' file (if '-t' option provided) +# +# - Public proof: text file(s) containing a list of salted file +# hashes. +# +# - Private proof: text file(s) containing: +# +# - A list of unsalted file hashes +# +# - A list of nonces used to generate the list of salted file hashes. +# +# - The list of salted file hashes +# +# - File attributes (size, relative path) +# +# The proof is produced by performing the following steps: +# +# - Use 'find' and GNU Coreutils digest algorithms to create +# numbered lists of file digests at specified directories. +# +# - Save these numbered lists of digests into text files within a +# proof directory. +# +# - If more than one specified directory is provided also process +# the files within the proof directory. +# +# - Use OpenTimestamps to create an 'ots' timestamp file +# for the final digest list file. (if '-t' option provided). +# +# - Use OpenTimestamps to upgrade each 'ots' timestamp file after a +# delay. (if '-w' option provided). +# +# Dependencies: coreutils, ots. See end of file +# +# Tested on: +# +# - GNU/Linux Debian 10 + + +# === VARIABLE INITIALIZATION AND FUNCTION DEFINITIONS === + +PATH="/usr/local/bin/:$PATH" # Add default OpenTimestamps path to PATH (necessary if cron used to call this script) +scriptHostname=$(hostname) # Save hostname of system running this script. +OTS_TIMEOUT="6h" # maximum time to run `ots --wait` (used via `timeout` command) + +# Declare array for storing directories whose contents will be hashed. +declare -a directoriesToProcess + +# Declare arrays for storing file paths, file index, file hashes, file +# hash nonces, the salted hashes, file sizes, file paths, file +# modification times, and proof file paths. +declare -a filePathsFull # array +declare -a filePathsIndex # array +declare -a fileHashes # array +declare -a fileHashNonces # array +declare -a fileHashesSalted # array +declare -a fileSizes # array +declare -a fileModTime # array +declare -g PROOF_DIRECTORY # global +declare -ag prvProofPaths # array, global +declare -ag pubProofPaths # array, global + +# Define 'echoerr' function which outputs text to stderr + # Note: function copied from https://stackoverflow.com/a/2990533 +echoerr() { + echo "$@" 1>&2; +} + +# Define script version. +SCRIPT_VERSION="bkpoe 0.1.0" + +# Save current date. +runDate=$(date +%Y%m%dT%H%M%S%z) +sleep 1 # Space out program run times by at least one second. +###echoerr "DEBUG: Current date is:"$runDate + +# Save working directory +runPath=$(pwd) +###echoerr "DEBUG: Current working directory is:"$runPath + +# Define proof directory basename +PROOF_DIRECTORY_BASE="$runDate""..""$scriptHostname""_bkpoe_proofs" + +# Define default proof directory +PROOF_DIRECTORY="$runPath"/"$PROOF_DIRECTORY_BASE" + +# Print information on how to use this bash script. +usage() { + + echo "USAGE:" + echo " bkpoe [ options ] DIRECTORIES" + echo + echo "OPTIONS:" + echo " -h, --help" + echo " Display help information." + echo + echo " -r, --recursive" + echo " Perform recursive hashing of directories." + echo + echo " -v, --verbose" + echo " Display debugging info." + echo + echo " -a, --algorithm [ algo ]" + echo " Specify GNU Coreutils hash command. Options include: " + echo " b2sum md5sum sha1sum sha224sum sha256sum sha384sum " + echo " sha512sum" + echo + echo " -m, --message [ string ]" + echo " Specify message to be included in filehash files." + echo + echo " -d, --delimiter [ string ]" + echo " Specify character to serve as delimiter in proof files. " + echo " (Default: ,) " + echo " -t, --timestamp" + echo " Create openTimestmaps proof file." + echo " -w, --wait" + echo " Pass '--wait' option to OpenTimestamps to 'wait " + echo " until a complete timestamp committed in the " + echo " Bitcoin blockchain is available instead of " + echo " returning immediately. " + echo " -o, --output" + echo " Specify output directory to save proof. Default is working" + echo " directory where script was run." +} + + +# Check that a string matches one of GNU Coreutils hash commands. +isValidDigestAlgo() { + # Syntax: isValidDigestAlgo [string] + # Description: Returns 1 if [string] is a valid GNU Coreutils hash function command. Returns 0 otherwise. + + # Exit if more than one argument provided. + ###echoerr "DEBUG: Arguments provided to isValidDigestAlgo() are:$@" + if [[ $# -ne 1 ]]; then + echoerr "ERROR: Illegal number of digest arguments provided." + exit 1 + fi + + # Exit if argument contains non-alphanumeric characters + if [[ "$@" =~ [^a-zA-Z0-9$] ]]; then + echoerr "ERROR: Illegal characters for digest argument provided." + exit 1 + fi + + local input=$1 #Save first argument provided to function as input for further processing. + + ###echoerr "DEBUG: Starting function to check validity of string as valid digest command." + + # Define array of valid hash algorithm commands present in by GNU coreutils package. List is current as of Debian Buster. List taken from https://packages.debian.org/buster/amd64/coreutils/filelist ). + local gnuCoreutilsAlgos=(b2sum md5sum sha1sum sha224sum sha256sum sha384sum sha512sum) + ###echoerr "DEBUG:Contents of \$gnuCoreutilsAlgos array is:${gnuCoreutilsAlgos[@]}" + + # Make input string lowercase + #input=${input,,} # Make all letters in $1 lowercase (see https://stackoverflow.com/questions/2264428/how-to-convert-a-string-to-lower-case-in-bash ) and save as $input + ###echoerr "DEBUG: User-specified hash algorithm is:$input" + + # Return true (0) if $gnuCoreutilsAlgos array contains string $input (see https://unix.stackexchange.com/a/177139 ) + for item in "${gnuCoreutilsAlgos[@]}"; do + ###echoerr "DEBUG: \$item is:$item" + ###echoerr "DEBUG: \$input is:$input" + if [ "$input" == "$item" ]; then + ###echoerr "DEBUG: $input present in \$gnuCoreutilsAlgos array." + return 0 + fi + done + + # Return false (1) otherwise. + echoerr "DEBUG: $input not recognized as valid hash algorithm name." + return 1 +} + +writeProofs() { + # Syntax: writeProofs [ loopIndex ] [ dirPath ] + + # Description: Construct private and public proofs for files + # contained within [ dirPath ]. Save file with date, [ loopIndex ], + # and base directory name within filename. + + # Required variables: + # - $runDate: used in creating proof directory + # - $PROOF_DIRECTORY: used as directory where proof files are saved + # - $OPTION_ALGORITHM: used to decide which hash algorithm to use + # - $OPTION_DELIMITER: used to determine which char to use as a delimiter in proof files + # - $OPTION_MESSAGE: used to determine message included in proof files + # - $OPTION_RECURSIVE: used to determine if files within subdirectories under [ dir Path ] directory should be included + + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG:This is the writeProofs() function. Provided arguments are:$@" + + # Determine scope of 'find' file search (recursive search or not) + if [[ $OPTION_RECURSIVE == "true" ]]; then + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: Performing recursive file search on directory $directoryToHash" + findMaxDepthOption="" # Do not specify -maxdepth option for find (causes 'find' to include all files recursively within [ dirPath ] + else + findMaxDepthOption="-maxdepth 1" # Specify "-maxdepth 1 " as option for 'find' so only files immediately within [ dirPath ] directory are included (no subdirectories). + fi + + # Save current date of loop + proofLoopRunDate=$(date +%Y%m%dT%H%M%S%z) + ###echoerr "DEBUG: Date and time of current loop is:"$proofLoopRunDate + + # Reset arrays. + filePathsFull=() + filePathsIndex=() + fileHashes=() + fileHashNonces=() + fileHashesSalted=() + fileSizes=() + fileModTime=() + + # Pass first argument provided to function as directory index. + local loopIndex=$1 + + # Pass second argument as directory to process + if [[ -d $2 ]]; then + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: Valid directory argument provided to writeProofs() function." + local directoryToHash=$2 + ## local directoryToHash=$(readlink -f $2) # Use absolutely full path (not used for privacy). + else + echoerr "ERROR: Invalid directory argument provided to writeProofs() function." + exit 1 + fi + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG:Processing directory:"$directoryToHash + local directoryToHashShort="$(basename "$directoryToHash")" + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG:Directory basename:"$directoryToHashShort + + # Populate filePathsFull array with file paths from 'find' results (see https://stackoverflow.com/a/54561526 ). + # Note: For creating array from output of find via readarray, see https://unix.stackexchange.com/a/263885 + # Note: For sorting output of find, see https://unix.stackexchange.com/a/34328 + readarray -d '' filePathsFull < <(find $directoryToHash $findMaxDepthOption -readable -type f -print0 | sort -z) # Populate filePathsFull array with sorted list of files present in $directoryToHash (and subdirectories if $OPTION_RECURSIVE set to 'true'. + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: The ${#filePathsFull[@]} elements of filePathsFull array are:${filePathsFull[@]}" # Show array length (see https://www.cyberciti.biz/faq/finding-bash-shell-array-length-elements/ ) + + # Construct index array from filePathsFull array for the for loops used to write proofs. + filePathsIndex=(${!filePathsFull[@]}) + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: The ${#filePathsIndex[@]} elements of filePathsIndex array are:${filePathsIndex[@]}" + + # Populate fileHashes with hashes of files listed in filePathsFull array + # Iterate through all elements of filePathsFull array using an integer index. (see https://stackoverflow.com/a/6723516 ) + for i in "${!filePathsIndex[@]}"; do + ###[[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG:The $i th element of the filePathsFull array is:${filePathsFull[i]}" + fileHashes[i]=$($OPTION_ALGORITHM "${filePathsFull[i]}" | awk '{ print $1 }') # Get hash value from GNU Coreutils hash command (see https://stackoverflow.com/a/3679803 ) + done + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: The ${#fileHashes[@]} elements of fileHashes array are:${fileHashes[@]}" + + # Populate fileHashNonces array with same number of elements as filePathsFull array. Each element is a random hexadecimal nonce of length matching OPTION_ALGORITHM. Hex nonce generated from 64-byte (512-bits) block from /dev/urandom. + for i in "${!filePathsIndex[@]}"; do + fileHashNonces[i]=$(dd bs=64 count=1 if=/dev/urandom 2>/dev/null | $OPTION_ALGORITHM | awk '{ print $1 }') + done + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: The ${#fileHashNonces[@]} elements of fileHashNonces array are:${fileHashNonces[@]}" + + # Populate fileHashesSalted array with salted digest for each file hash corresponding nonce. + for i in "${!filePathsIndex[@]}"; do + fileHashesSalted[i]=$(echo -n "${fileHashes[i]}${fileHashNonces[i]}" | $OPTION_ALGORITHM | awk '{ print $1 }') + done + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: The ${#fileHashesSalted[@]} elements of fileHashesSalted array are:${fileHashesSalted[@]}" + + # Populate fileSizes array with file sizes (in bytes). + for i in "${!filePathsIndex[@]}"; do + fileSizes[i]=$(wc -c "${filePathsFull[i]}" | awk '{ print $1 }') + done + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: The ${#fileSizes[@]} elements of fileSizes array are:${fileSizes[@]}" + + # Populate fileModTime array with file modification times (in ISO-8601 format) + for i in "${!filePathsIndex[@]}"; do + fileModTime[i]=$(date --iso-8601=seconds -r "${filePathsFull[i]}" | awk '{ print $1 }') + done + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: The ${#fileModTime[@]} elements of fileModTime array are:${fileModTime[@]}" + + # ==== GENERATE PROOF FILES FROM HASH LIST(S) ==== + + # Define output file names and paths. + local PROOF_PRIVATE_FILENAME=$proofLoopRunDate"_"$loopIndex"_prv.."$scriptHostname"_"$directoryToHashShort"_"$OPTION_ALGORITHM"_proof_private.txt" + local PROOF_PUBLIC_FILENAME=$proofLoopRunDate"_"$loopIndex"_pub.."$scriptHostname"_"$directoryToHashShort"_"$OPTION_ALGORITHM"_proof_public.txt" + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: \$PROOF_PRIVATE_FILENAME is $PROOF_PRIVATE_FILENAME" + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: \$PROOF_PUBLIC_FILENAME is $PROOF_PUBLIC_FILENAME" + PROOF_PRIVATE_FILEPATH="$PROOF_DIRECTORY"/"$PROOF_PRIVATE_FILENAME" + PROOF_PUBLIC_FILEPATH="$PROOF_DIRECTORY"/"$PROOF_PUBLIC_FILENAME" + + # Create private proof and public proof files. + touch "$PROOF_PRIVATE_FILEPATH" + touch "$PROOF_PUBLIC_FILEPATH" + + # Abbreviate OPTION_DELIMITER to local variable DELIM + local DELIM=$OPTION_DELIMITER + + # Define private proof first-line headers (date, version, algorithm, message) + local PROOF_PRIVATE_HEADER1="$proofLoopRunDate$DELIM$SCRIPT_VERSION$DELIM$OPTION_ALGORITHM$DELIM$OPTION_MESSAGE" + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: \$PROOF_PRIVATE_HEADER1 is $PROOF_PRIVATE_HEADER1" + # Define public proof first-line headers (date, version, algorithm, message) + local PROOF_PUBLIC_HEADER1="$proofLoopRunDate$DELIM$SCRIPT_VERSION$DELIM$OPTION_ALGORITHM$DELIM$OPTION_MESSAGE" + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: \$PROOF_PUBLIC_HEADER1 is $PROOF_PUBLIC_HEADER1" + + # Define private proof second-line column labels (index, digest, nonce, salted digest, mdate, size, filepath) + local PROOF_PRIVATE_HEADER2="index"$DELIM"digest"$DELIM"nonce"$DELIM"digest_salted"$DELIM"mdate"$DELIM"size_bytes"$DELIM"file_path" + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: \$PROOF_PRIVATE_HEADER2 is $PROOF_PRIVATE_HEADER2" + # Define public proof second-line column labels (index, salted digest) + local PROOF_PUBLIC_HEADER2="index"$DELIM"digest_salted" + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: \$PROOF_PUBLIC_HEADER2 is $PROOF_PUBLIC_HEADER2" + + # Write headers to proof files + echo $PROOF_PRIVATE_HEADER1 >> "$PROOF_PRIVATE_FILEPATH" + echo $PROOF_PRIVATE_HEADER2 >> "$PROOF_PRIVATE_FILEPATH" + echo $PROOF_PUBLIC_HEADER1 >> "$PROOF_PUBLIC_FILEPATH" + echo $PROOF_PUBLIC_HEADER2 >> "$PROOF_PUBLIC_FILEPATH" + + # Loop to append array contents. + for i in "${!filePathsIndex[@]}"; do + # Append to private proof: filePathsIndex[i],fileHashes[i],fileHashNonces[i],fileHashesSalted[i],fileModTime[i],fileSizes[i],filePathsFull[i] + ###[[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: Writing line to $PROOF_PRIVATE_FILEPATH" + echo ${filePathsIndex[i]}$DELIM${fileHashes[i]}$DELIM${fileHashNonces[i]}$DELIM${fileHashesSalted[i]}$DELIM${fileModTime[i]}$DELIM${fileSizes[i]}$DELIM${filePathsFull[i]} >> "$PROOF_PRIVATE_FILEPATH" + # Append to public proof: filePathsIndex[i],fileHashesSalted[i] + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: Writing line to $PROOF_PUBLIC_FILEPATH" + echo ${filePathsIndex[i]}$DELIM${fileHashesSalted[i]} >> $PROOF_PUBLIC_FILEPATH + done + + # Save file paths to global variables prvProofPaths and pubProofPaths to allow other functions to manipulate files. + prvProofPaths+=($PROOF_PRIVATE_FILEPATH) + pubProofPaths+=($PROOF_PUBLIC_FILEPATH) + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: The ${#prvProofPaths[@]} elements of prvProofPaths array are:${prvProofPaths[@]}" + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: The ${#pubProofPaths[@]} elements of pubProofPaths array are:${pubProofPaths[@]}" + + # Reset arrays. + filePathsFull=() + filePathsIndex=() + fileHashes=() + fileHashNonces=() + fileHashesSalted=() + fileSizes=() + fileModTime=() +} + + +# === INPUT PROCESSING === +# Check for presence of options. +###echoerr "DEBUG: === INPUT PROCESSING START ===" + +# Process initial option arguments (see https://jonalmeida.com/posts/2013/05/26/different-ways-to-implement-flags-in-bash/ and https://likegeeks.com/linux-bash-scripting-awesome-guide-part3/ ) +while [ ! $# -eq 0 ] # While number of arguments ($#) is not (!) equal to (-eq) zero (0). +do + case "$1" in # Check first of remaining arguments to see if it matches one of strings below. + --help | -h) + # Code to run if $1 matched "--help" or "-h": + ###echoerr "DEBUG: This is the help information." + usage # Run usage function to display helpful info to user. + exit 1 + ;; + --recursive | -r) + # Code to run if $1 matched "--recursive" or "-r": + ###echoerr "DEBUG: Recursive option activated." + OPTION_RECURSIVE="true" + ;; + --verbose | -v) + # Code to run if $1 matched "--verbose" or "-v": + ###echoerr "DEBUG: Verbose option activated." + OPTION_VERBOSE="true" + ;; + --algorithm | -a) + # Code to run if $1 matched "--algorithm" or "-a": + + ###echoerr "DEBUG: Selecting hash algorithm." + # Check that OPTION_ALGORITHM variable hasn't already been set. + # Check that OPTION_ALGORITHM contains only alphanumeric characters. + # Check that argument following '-a' or '--algorithm' ($2) is valid algorithm. + if [[ ! -v OPTION_ALGORITHM ]] && [[ ! "$2" =~ [^a-zA-Z0-9$] ]] && isValidDigestAlgo $2; then + ###echoerr "DEBUG: Valid hash algorithm provided." + OPTION_ALGORITHM=$2 # Save argument following '-a' or '--algorithm' to $OPTION_ALGORITHM + shift # Remove an additional argument so the additional algorithm argument $2 is properly removed at the end of the while loop. + else + echoerr "ERROR: Invalid hash algorithm argument provided:""$2" + exit 1 + fi + ;; + --message | -m) + # Code to run if $1 matched "--message" or "-m": + ###echoerr "DEBUG: Specifying header message." + # Check that OPTION_MESSAGE variable hasn't already been set. + if [[ ! -v OPTION_MESSAGE ]]; then + OPTION_MESSAGE=$2 # Save argument following '-m' or '--message' to $OPTION_MESSAGE + shift # Remove an additional argument so the additional algorithm argument $2 is properly removed at the end of the while loop. + fi + ;; + --delimiter | -d) + # Code to run if $1 matched "--delimiter" or "-d": + ###echoerr "DEBUG: Specifying delimiter." + # Check that OPTION_DELIMITER variable hasn't already been set. + if [[ ! -v OPTION_DELIMITER ]]; then + OPTION_DELIMITER=$2 # Save argument following '-d' or '--delimiter' to $OPTION_DELIMITER + shift # Remove an additional argument so the additional delimiter argument $2 is properly removed at the end of the while loop. + fi + ;; + --timestamp | -t) + # Code to run if $1 matched "--timestamp" or "-t": + echoerr "DEBUG: OpenTimestamps option selected." + # Check that ots command exists. (see https://stackoverflow.com/a/677212 ) + if command -v ots >/dev/null 2>&1 ; then + echoerr "DEBUG: OpenTimestampscommand ('ots') detected." + OPTION_OPENTIMESTAMPS="true" + else + echo >&2 "I require the ots (OpenTimestamps) command but it's not installed. Aborting." + exit 1 + fi + ;; + --wait | -w) + # Code to run if $1 matched "--wait" or "-w": + echoerr "DEBUG: OpenTimestamps '--wait' option selected." + OPTION_OPENTIMESTAMPS_WAIT="true" + ;; + --output | -o) + # Code to run if $1 matched "--output" or "-o": + # Check that argument $2 is a directory. + if [[ -d $2 ]]; then + echoerr "DEBUG: Specified ""$PROOF_DIRECTORY_BASE"" in ""$2"" as directory for saving proof files." + PROOF_DIRECTORY="$2"/"$PROOF_DIRECTORY_BASE" + echoerr "DEBUG: Proof Directory is:""$PROOF_DIRECTORY" + shift # Remove an additional argument so the additional delimiter argument $2 is properly removed at the end of the while loop. + else + echoerr "ERROR: Invalid argument provided as output directory for proof files:""$2" + exit 1 + fi + ;; + *) + # Code to run if $1 isn't any of the above cases: + # Check that remaining argument is a directory. + if [[ -d $1 ]]; then + ###echoerr "DEBUG: Remaining argument is directory. Adding to \$directoriesToProcess array." + # Add directory argument $1 to directory array + directoriesToProcess+=($1) # (see https://stackoverflow.com/a/1951523 ) + else + echoerr "ERROR: Remaining argument is not a directory or valid option. Exiting." + exit 1 + fi + ;; + esac + shift # Remove first argument ($1) so remaining arguments can be processed on next loop. +done + +# If OPTION_ALGORITHM is not set then set it to 'sha256sum' by default. +if [[ ! -v OPTION_ALGORITHM ]]; then + echoerr "DEBUG: No hash algorithm set. Defaulting to sha256sum." + OPTION_ALGORITHM="sha256sum" +fi + +# If OPTION_DELIMITER is not set then set it to ',' by default. +if [[ ! -v OPTION_DELIMITER ]]; then + ###echoerr "DEBUG: No delimiter set. Defaulting to ','." + OPTION_DELIMITER="," +fi + +# Exit program if directoriesToProcess array is empty. +if [[ ${#directoriesToProcess[@]} -eq 0 ]]; then + echoerr "ERROR: No valid directory specified." + exit 1 +fi + +###echoerr "DEBUG: === INPUT PROCESSING END ===" + +# === MAIN PROGRAM === +[[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: === MAIN PROGRAM START ===" +[[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: Verbose option active." +[[ $OPTION_VERBOSE == "true" ]] && [[ $OPTION_RECURSIVE == "true" ]] && echoerr "DEBUG: Recursive option active." +[[ $OPTION_VERBOSE == "true" ]] && [[ -v OPTION_ALGORITHM ]] && echoerr "DEBUG: Selected Hash algorithm is:""$OPTION_ALGORITHM" +[[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: Directories to process are: ${directoriesToProcess[@]}" +[[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: Message to add to proof is: $OPTION_MESSAGE" +[[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: Proof directory is:""$PROOF_DIRECTORY" +[[ $OPTION_VERBOSE == "true" ]] && [[ $OPTION_OPENTIMESTAMPS == "true" ]] && echoerr "DEBUG: OpenTimestamps option active." +[[ $OPTION_VERBOSE == "true" ]] && [[ $OPTION_OPENTIMESTAMPS_WAIT == "true" ]] && echoerr "DEBUG: OpenTimestamps '--wait' option selected." +# Create proof directory +mkdir "$PROOF_DIRECTORY" +echoerr "Proof directory created at:""$PROOF_DIRECTORY" + +## TEMP REF: +# declare -a filePathsFull # array +# declare -a filePathsIndex # array +# declare -a fileHashes # array +# declare -a fileHashNonces # array +# declare -a fileHashesSalted # array +# declare -a fileSizes # array +# declare -a fileModTime # array +# declare -ag prvProofPaths # array, global +# declare -ag pubProofPaths # array, global + +# ==== GENERATE HASH LIST(S) ==== +[[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: ==== GENERATING HASH LIST(S) ====" + +# Assemble and write proofs to disk for all specified directories. + +# Generate proofs from all provided directories. +for j in "${!directoriesToProcess[@]}"; do + writeProofs $j "${directoriesToProcess[j]}" +done + +# Decide if superproof is required. +if [[ ${#directoriesToProcess[@]} -eq 1 ]]; then + # If only one directory provided then no further proofs required. + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: Only 1 directory processed. No superproof required." +elif [[ ${#directoriesToProcess[@]} -gt 1 ]]; then + # If multiple directories provided then create superproof from files in PROOF_DIRECTORY. + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: More than 1 directory processed. Proceeding to generate superproof from PROOF_DIRECTORY." + writeProofs "S" $PROOF_DIRECTORY # Create proof using proof files which are stored in PROOF_DIRECTORY +fi + +# ==== GENERATE HASH LIST(S) REPORT ===== +echoerr "Proof files written to $PROOF_DIRECTORY directory:" +for i in ${!prvProofPaths[@]}; do + echo $(basename "${prvProofPaths[i]}") + echo $(basename "${pubProofPaths[i]}") +done + +# ==== GENERATE OPENTIMESTAMPS TIMESTAMP FILE(S) ==== + +# Determine if OpenTimestamp actions to be performed. +if [[ $OPTION_OPENTIMESTAMPS == "true" ]]; then + # Identify target for OpenTimeStamps script. + otsTarget=${pubProofPaths[-1]} # Select final element in pubProofPaths array as target for 'ots' command. + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: otsTarget is:""$otsTarget" + + # Set wait option as determined by OPTION_OPENTIMESTAMPS_WAIT variable. Set timeout length. + if [[ $OPTION_OPENTIMESTAMPS_WAIT == "true" ]]; then + otsWaitOption="--wait" # Specify '--wait' option for ots command. + otsWaitTimeCmd="timeout ""$OTS_TIMEOUT" # Specify timeout length (6 hours) for ots command. + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: OpenTimestamps '--wait' option active." + else + otsWaitOption="" + otsWaitTimeCmd="" + [[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: OpenTimestamps '--wait' option not active." + fi + + # Generate OpenTimeStamps proof file. + pushd "$PROOF_DIRECTORY" 1>/dev/null 2>/dev/null # temporarily change working dir to PROOF_DIRECTORY, suppress stdout and stderr + $otsWaitTimeCmd ots $otsWaitOption stamp $otsTarget + echoerr "OpenTimestamps operation successful! $otsTarget created." + echoerr "Use 'ots info [ filename ]' for more info." + echoerr "Use 'ots upgrade' if '--wait' option not used to make .ots file independently-verifiable." + echoerr "See https://opentimestamps.org/ for more information." + popd 1>/dev/null 2>/dev/null # reverse temporary change to working dir, suppress stdout and stderr + +fi + + +[[ $OPTION_VERBOSE == "true" ]] && echoerr "DEBUG: === MAIN PROGRAM END ===" +# Exit program successfully. +exit 0 + + +# == Dependencies == +# +# - bash, find, sort, echo, mkdir, basename, awk, wc, date, dd, +# readlink, touch, ots, + +# - GNU bash, version 5.0.3(1)-release (x86_64-pc-linux-gnu) +# Copyright (C) 2019 Free Software Foundation, Inc. +# License GPLv3+: GNU GPL version 3 or later +# This is free software; you are free to change and redistribute it. +# There is NO WARRANTY, to the extent permitted by law. + +# - find (GNU findutils) 4.6.0.225-235f +# Copyright (C) 2019 Free Software Foundation, Inc. +# License GPLv3+: GNU GPL version 3 or later . +# This is free software: you are free to change and redistribute it. +# There is NO WARRANTY, to the extent permitted by law. +# Written by Eric B. Decker, James Youngman, and Kevin Dalley. +# Features enabled: D_TYPE O_NOFOLLOW(enabled) LEAF_OPTIMISATION FTS(FTS_CWDFD) CBO(level=2) + +# - sort (GNU coreutils) 8.30 +# Copyright (C) 2018 Free Software Foundation, Inc. +# License GPLv3+: GNU GPL version 3 or later . +# This is free software: you are free to change and redistribute it. +# There is NO WARRANTY, to the extent permitted by law. +# Written by Mike Haertel and Paul Eggert. + +# - echo (GNU coreutils) 8.30 +# Copyright (C) 2018 Free Software Foundation, Inc. +# License GPLv3+: GNU GPL version 3 or later . +# This is free software: you are free to change and redistribute it. +# There is NO WARRANTY, to the extent permitted by law. +# Written by Brian Fox and Chet Ramey. + +# - mkdir (GNU coreutils) 8.30 +# Copyright (C) 2018 Free Software Foundation, Inc. +# License GPLv3+: GNU GPL version 3 or later . +# This is free software: you are free to change and redistribute it. +# There is NO WARRANTY, to the extent permitted by law. +# Written by David MacKenzie. + +# - basename (GNU coreutils) 8.30 +# Copyright (C) 2018 Free Software Foundation, Inc. +# License GPLv3+: GNU GPL version 3 or later . +# This is free software: you are free to change and redistribute it. +# There is NO WARRANTY, to the extent permitted by law. +# Written by David MacKenzie. + +# - GNU Awk 4.2.1, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.1.2) +# Copyright (C) 1989, 1991-2018 Free Software Foundation. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + +# - wc (GNU coreutils) 8.30 +# Copyright (C) 2018 Free Software Foundation, Inc. +# License GPLv3+: GNU GPL version 3 or later . +# This is free software: you are free to change and redistribute it. +# There is NO WARRANTY, to the extent permitted by law. +# Written by Paul Rubin and David MacKenzie. + +# - date (GNU coreutils) 8.30 +# Copyright (C) 2018 Free Software Foundation, Inc. +# License GPLv3+: GNU GPL version 3 or later . +# This is free software: you are free to change and redistribute it. +# There is NO WARRANTY, to the extent permitted by law. +# Written by David MacKenzie. + +# - dd (coreutils) 8.30 +# Copyright (C) 2018 Free Software Foundation, Inc. +# License GPLv3+: GNU GPL version 3 or later . +# This is free software: you are free to change and redistribute it. +# There is NO WARRANTY, to the extent permitted by law. +# Written by Paul Rubin, David MacKenzie, and Stuart Kemp. + +# - readlink (GNU coreutils) 8.30 +# Copyright (C) 2018 Free Software Foundation, Inc. +# License GPLv3+: GNU GPL version 3 or later . +# This is free software: you are free to change and redistribute it. +# There is NO WARRANTY, to the extent permitted by law. +# Written by Dmitry V. Levin. + +# - touch (GNU coreutils) 8.30 +# Copyright (C) 2018 Free Software Foundation, Inc. +# License GPLv3+: GNU GPL version 3 or later . +# This is free software: you are free to change and redistribute it. +# There is NO WARRANTY, to the extent permitted by law. +# Written by Paul Rubin, Arnold Robbins, Jim Kingdon, +# David MacKenzie, and Randy Smith. + +# - ots v0.7.0 ( https://github.com/opentimestamps/opentimestamps-client ) +# The OpenTimestamps Client is free software: you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# The OpenTimestamps Client is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# below for more details.