mgutt Posted June 16, 2020 Posted June 16, 2020 (edited) Description filecryptor is a shell script that reads all files in a source directory and copies them encrypted through AES-256 in a target directory. The passphrase must be added through a file named "filecryptor.key" that has to be located in the root of the source directory. Your password is salted with the file's timestamp and hashed with PBKDF2 10.000x times. This is the default value of filecryptor. You can change it, but you need to remind the exact value or you are not able to decrypt the files anymore! Feel free to upload your files to a cloud after encryption has been finished! You can execute this script by using the CA User Scripts plugin. Features - encrypt files of a source to a target directory with AES-256 by your password - encryption is hardened by iterating your password through PBKDF2 10.000x times which slows down bruteforce attacks - encryption is hardened by salting your password through the file's timestamp leaving no chance to rainbow table attacks - decrypt files of a source folder - resume if last script execution has been interrupted (delete filecryptor.last in target dir to fully restart) - skip_files_last_seconds allows skipping files that are (partially) written at the moment - dry_run allows testing filecryptor - already existing files with a newer or same timestamp will be skipped #!/bin/sh # ##################################### # filecryptor # Version: 0.3 # Author: Marc Gutt # # Description: # Copies and encrypts files from a source to a target dir. This scripts uses # AES encryption with the file modification time as salt. By that re-encrypting # produces identical files making it easier for rsync/rclone to skip already # transfered files. # # How-to encrypt: # 1.) Add a text file with the name "filecryptor.key" and your encryption password as content in the source directory. # 2.) Set your source and target directories # 3.) Execute this script # # How-to decrypt: # 1.) Backup your encrypted files # 2.) Add a text file with the name "filecryptor.key" and your encryption password as content in the source directory. # 3.) Set ONLY the source directory (encrypted files will be overwritten through their decrypted version!) # 4.) Execute this script # # Notes: # - A public salt should be safe (https://crypto.stackexchange.com/a/59180/41422 & https://stackoverflow.com/a/3850335/318765) # - After encrypting you could use rsync with "--remove-source-files" to move the encrypted files to a final target directory # - rclone has a "move" mode to move the encrypted files to a cloud # - You can set the PBKDF2 iterations through "iter". A higher number of iterations adds more security but is slower # (https://en.wikipedia.org/wiki/PBKDF2#Purpose_and_operation & https://security.stackexchange.com/a/3993/2296) # # Changelog: # 0.3 # - set your own PBKDF2 iteration count through "iter" setting # - salt is padded with zeros to avoid "hex string is too short, padding with zero bytes to length" message on openssl output # - decryption now supports resume # - empty openssl files are deleted on decryption fails # - encryption of already existing files in the target will be skipped as long the source files aren't newer # 0.2 # - skip dirs on resume (not only files) # - bug fix: endless looping empty dirs # 0.1 # - first release # # Todo: # - optional filename encryption # - use a different filename if decrypted filename "${file}.filecryptor" already exists # - add "overwrite=true" setting and check file existence before executing openssl # ##################################### # settings source="/mnt/disks/THOTH_Photo" target="/mnt/user/photo" skip_files_last_seconds=false dry_run=false iter=10000 # remind this number or you are not able to decrypt your files anymore! # check settings source=$([[ "${source: -1}" == "/" ]] && echo "${source%?}" || echo "$source") target=$([[ "${target: -1}" == "/" ]] && echo "${target%?}" || echo "$target") target=$([[ $target == 0 ]] && echo "false" || echo "$target") target=$([[ $target == "" ]] && echo "false" || echo "$target") skip_files_last_seconds=$([[ $skip_files_last_seconds == 0 ]] && echo "false" || echo "$skip_files_last_seconds") dry_run=$([[ $dry_run == 0 ]] && echo "false" || echo "$dry_run") dry_run=$([[ $dry_run == 1 ]] && echo "true" || echo "$dry_run") # defaults pwfile="${source}/filecryptor.key" resume="${target}/filecryptor.last" # check if passphrase exists if [[ ! -f $pwfile ]]; then echo "Error! filecryptor did not found ${source}/filecryptor.key" exit 1 fi # check if we have a starting point if [[ -f $resume ]]; then last_file=$( < $resume) fi function filecryptor() { path=$1 echo "Parsing $path ..." for file in "$path"/*; do # regular file if [ -f "$file" ]; then # skip passphrase file if [[ $file == $pwfile ]]; then echo "Skip $pwfile" continue fi # skip files until we reach our starting point if [[ -n $last_file ]]; then if [[ $file != $last_file ]]; then echo "Skip $file" else echo "Found the last processed file $last_file" last_file="" fi continue fi file_time=$(stat -c %Y "$file") # file modification time # decrypt file if [[ $target == "false" ]]; then echo "Decrypt file ${file}" if [[ $dry_run != "true" ]]; then if openssl aes-256-cbc -d -iter 10000 -in "$file" -out "${file}.filecryptor" -pass file:"${pwfile}"; then rm "$file" mv "${file}.filecryptor" "$file" touch --date=@${file_time} "${file}" else rm "${file}.filecryptor" # cleanup on fail fi fi # remember this file as starting point for the next execution (if interrupted) if [[ $dry_run != "true" ]]; then echo "$file" > "${resume}" fi continue fi # skip new files if [[ "$skip_files_older_seconds" =~ ^[0-9]+$ ]]; then compare_time=$(($file_time + $skip_files_older_seconds)) current_time=$(date +%s) # is the file old enough? if [[ $compare_time -gt $current_time ]]; then continue fi fi dirname=$(dirname "$file") dirname="${dirname/$source/}" # remove source path from dirname file_basename=$(basename -- "$file") # skip already existing files with same timestamp if [ -f "${target}${dirname}/${file_basename}" ]; then target_file_time=$(stat -c %Y "$file") # file modification time if [[ $target_file_time -ge $file_time ]];then echo "Skipped ${file} as it already exists in target" continue fi fi # create parent dirs echo "Create parent dirs ${target}${dirname}" if [[ $dry_run != "true" ]]; then mkdir -p "${target}${dirname}" fi # encrypt file echo "Create encrypted file ${target}${dirname}/${file_basename}" if [[ $dry_run != "true" ]]; then salt="${file_time}0000000000000000" salt=${salt:0:16} openssl aes-256-cbc -iter $iter -in "$file" -out "${target}${dirname}/${file_basename}" -S $salt -pass file:"${pwfile}" fi # modification time echo "Set original file modification time" if [[ $dry_run != "true" ]]; then touch --date=@${file_time} "${target}${dirname}/${file_basename}" # https://unix.stackexchange.com/a/36765/101920 fi # remember this file as starting point for the next execution (if interrupted) if [[ $dry_run != "true" ]]; then echo "$file" > "${resume}" fi # dir elif [ -d "$file" ]; then # skip dir until we reach our starting point if [[ -n $last_file ]]; then if [[ $last_file != "${file}"* ]]; then echo "Skip $file" continue else echo "Found the last processed dir $file" fi fi filecryptor "$file" fi done } filecryptor "$source" # clean up if [[ $dry_run != "true" ]]; then rm "${resume}" fi exit # encrypt example if file_time=$(stat -c %Y "file.txt"); then openssl aes-256-cbc -iter 10000 -in "file.txt" -out "file.enc" -S $file_time -pass file:"filecryptor.key" touch --date=@${file_time} "file.enc" fi # decrypt example if file_time=$(stat -c %Y "file.enc") && openssl aes-256-cbc -d -iter $iter -in "file.enc" -out "file.enc.filecryptor" -pass file:"filecryptor.key"; then rm "file.enc" mv "file.enc.filecryptor" "file.txt" touch --date=@${file_time} "file.txt" fi Edited June 20, 2020 by mgutt Quote
mgutt Posted June 17, 2020 Author Posted June 17, 2020 Released Version 0.2: Quote # - skip dirs on resume (not only files) # - bug fix: endless looping empty dirs Quote
mgutt Posted June 20, 2020 Author Posted June 20, 2020 Released Version 0.3: Quote # - set your own PBKDF2 iteration count through "iter" setting # - salt is padded with zeros to avoid "hex string is too short, padding with zero bytes to length" message on openssl output # - decryption now supports resume, too # - empty openssl files are deleted on decryption fails # - encryption of already existing files in the target will be skipped as long the source files aren't newer Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.