diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..81b8298 --- /dev/null +++ b/README.rst @@ -0,0 +1,15 @@ +noirbox +====== + +Cloud-KMS based repo cryptography inspired by Stack Exchange's blackbox. + +Installation +------------- + +Just add ``bin/`` to your ``PATH``. + +Documentation +------------- + +Right now, just look at the usage comments for all the scripts in ``bin/``. + diff --git a/bin/_noirbox-common b/bin/_noirbox-common new file mode 100755 index 0000000..3877d95 --- /dev/null +++ b/bin/_noirbox-common @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +set -e -u -o pipefail + +key_size=256 + +up_to_noirbox_root() { + until [[ -d .noirbox ]]; do + cd .. + if [[ "$(pwd)" = "/" ]]; then + echo "No .noirbox directory found. Maybe noirbox-init?" + exit 1 + fi + done +} + +read_config() { + noirbox_dir="$(realpath .noirbox)" + location="$(cat "$noirbox_dir/config.yaml" | yq -r .location)" + key="$(cat "$noirbox_dir/config.yaml" | yq -r .key)" + keyring="$(cat "$noirbox_dir/config.yaml" | yq -r .keyring)" +} + +cat_file() { + file="$1" + local_key="$2" + # black magic to capture only stderr: https://unix.stackexchange.com/questions/474177/how-to-redirect-stderr-in-a-variable-but-keep-stdout-in-the-console + set +e + { err="$(echo "$local_key" | gpg --cipher-algo AES256 --batch -d --passphrase-fd 0 "$file.gpg" 2>&1 >&3 3>&-)"; } 3>&1 + retval=$? + set -e + if [[ $retval -ne 0 ]]; then + echo "$err" + exit $retval + fi +} + +decrypt_file() { + file="$1" + local_key="$2" + cat_file "$file" "$local_key" > "$file" +} + +encrypt_file() { + file="$1" + local_key="$2" + rm -f "$file.gpg" + echo "$local_key" | gpg --cipher-algo AES256 --batch -c --passphrase-fd 0 "$file" +} + + + +verbose="no" +debug="no" + +for arg in "${@}"; do + opt="$(echo "$arg" | cut -d= -f1)" + val="$(echo "$arg" | cut -d= -f2)" + case "$opt" in + "--verbose") + verbose="yes" + ;; + "--debug") + debug="yes" + ;; + esac +done + +if [[ "$verbose" = "yes" ]]; then + set -v +fi + +if [[ "$debug" = "yes" ]]; then + set -x +fi + diff --git a/bin/noirbox-cat b/bin/noirbox-cat new file mode 100755 index 0000000..8985d17 --- /dev/null +++ b/bin/noirbox-cat @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Output contents of an encrypted file +# Usage: noirbox-cat filename + +set -e -u -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +. "$script_dir/_noirbox-common" + +up_to_noirbox_root +read_config + +local_key="$(gcloud kms decrypt \ + --location="$location" \ + --keyring="$keyring" \ + --key="$key" \ + --plaintext-file=- \ + --ciphertext-file="$noirbox_dir/local-key.enc")" + +file="${1%.gpg}" + +cat_file "$file" "$local_key" + diff --git a/bin/noirbox-decrypt-all-files b/bin/noirbox-decrypt-all-files new file mode 100755 index 0000000..6fbf210 --- /dev/null +++ b/bin/noirbox-decrypt-all-files @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Decrypts all files, for pre-deployment use. +# Usage: noirbox-decrypt-all-files +# Warning: Be sure to ``noirbox-shred-all-files`` afterwards. + +set -e -u -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +. "$script_dir/_noirbox-common" + +up_to_noirbox_root +read_config + +local_key="$(gcloud kms decrypt \ + --location="$location" \ + --keyring="$keyring" \ + --key="$key" \ + --plaintext-file=- \ + --ciphertext-file="$noirbox_dir/local-key.enc")" + +while IFS="" read -r file +do + decrypt_file "$file" "$local_key" +done < "$noirbox_dir/noirbox-files.txt" + diff --git a/bin/noirbox-decrypt-file b/bin/noirbox-decrypt-file new file mode 100755 index 0000000..626ac88 --- /dev/null +++ b/bin/noirbox-decrypt-file @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Decrypts a single file into the filesystem. +# Usage: noirbox-decrypt-file filenme + +set -e -u -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +. "$script_dir/_noirbox-common" + +up_to_noirbox_root +read_config + +local_key="$(gcloud kms decrypt \ + --location="$location" \ + --keyring="$keyring" \ + --key="$key" \ + --plaintext-file=- \ + --ciphertext-file="$noirbox_dir/local-key.enc")" + +file="${1%.gpg}" + +decrypt_file "$file" "$local_key" + diff --git a/bin/noirbox-deregister-file b/bin/noirbox-deregister-file new file mode 100755 index 0000000..6f6be55 --- /dev/null +++ b/bin/noirbox-deregister-file @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Removes a file from the list of encrypted files +# Usage: noirbox-deregister-file filename + +set -e -u -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +. "$script_dir/_noirbox-common" + +up_to_noirbox_root +read_config + +old_file="${1%.gpg}" + +found=no +while IFS="" read -r file +do + if [[ "$file" = "$old_file" ]]; then + found=yes + break + fi +done < "$noirbox_dir/noirbox-files.txt" + +if [[ "$found" = "no" ]]; then + echo "$old_file is not registered" + exit 1 +fi + +files="$(while IFS="" read -r file +do + if [[ "$file" != "$old_file" ]]; then + echo "$file" + fi +done < "$noirbox_dir/noirbox-files.txt")" +echo "$files" > "$noirbox_dir/noirbox-files.txt" + +gitignore="$(while IFS="" read -r file +do + if [[ "$file" != "$old_file" ]]; then + echo "$file" + fi +done < ".gitignore")" +echo "$gitignore" > ".gitignore" + +rm -f "$old_file" +git rm -f "$old_file.gpg" diff --git a/bin/noirbox-edit b/bin/noirbox-edit new file mode 100755 index 0000000..d6deeed --- /dev/null +++ b/bin/noirbox-edit @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Edits a file in your $EDITOR. +# Usage: noirbox-edit filename + +set -e -u -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +. "$script_dir/_noirbox-common" + +up_to_noirbox_root +read_config +file="${1%.gpg}" + +"$script_dir/noirbox-edit-start" "${@}" +"$EDITOR" "$file" +"$script_dir/noirbox-edit-end" "${@}" diff --git a/bin/noirbox-edit-end b/bin/noirbox-edit-end new file mode 100755 index 0000000..3b3158d --- /dev/null +++ b/bin/noirbox-edit-end @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Call this after ``noirbox-edit-start``ing a file. +# Usage: noirbox-edit-end filename + +set -e -u -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +exec "$script_dir/noirbox-encrypt-file" "${@}" +git add "${1%.gpg}.gpg" diff --git a/bin/noirbox-edit-start b/bin/noirbox-edit-start new file mode 100755 index 0000000..676eae4 --- /dev/null +++ b/bin/noirbox-edit-start @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# If you want to edit using your IDE or similar, use this, and then noirbox-edit-end afterwards. +# Usage: noirbox-edit-start filename + +set -e -u -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +exec "$script_dir/noirbox-decrypt-file" "${@}" diff --git a/bin/noirbox-encrypt-file b/bin/noirbox-encrypt-file new file mode 100755 index 0000000..08eaa14 --- /dev/null +++ b/bin/noirbox-encrypt-file @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Encrypts a single file +# Usage: noirbox-encrypt-file filename + +set -e -u -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +. "$script_dir/_noirbox-common" + +up_to_noirbox_root +read_config + +local_key="$(gcloud kms decrypt \ + --location="$location" \ + --keyring="$keyring" \ + --key="$key" \ + --plaintext-file=- \ + --ciphertext-file="$noirbox_dir/local-key.enc")" + +file="${1%.gpg}" + +encrypt_file "$file" "$local_key" + diff --git a/bin/noirbox-init b/bin/noirbox-init new file mode 100755 index 0000000..5ee911b --- /dev/null +++ b/bin/noirbox-init @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +# Initializes noirbox in your directory. Expects to be in a git repo. +# Usage: noirbox-init --location=location --key=key --keyring=keyring + +set -e -u -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +. "$script_dir/_noirbox-common" + +mkdir -p .noirbox +noirbox_dir="$(realpath .noirbox)" + +keyring="" +location="" +key="" +create_key="yes" +force="" + +for arg in "${@}"; do + opt="$(echo "$arg" | cut -d= -f1)" + val="$(echo "$arg" | cut -d= -f2)" + case "$opt" in + "--keyring") + keyring="$val" + ;; + "--location") + location="$val" + ;; + "--key") + key="$val" + ;; + "--create-key") + create_key="$val" + ;; + "--force") + force="yes" + ;; + esac +done + +if [[ -d .noirbox ]] && [[ "$force" != "yes" ]]; then + echo "A .noirbox directory already exists; this noirbox seems already initialized." + exit 1 +fi + + +if [[ -z "$keyring" ]] || [[ -z "$location" ]] || [[ -z "$key" ]]; then + echo "Please enter location (e.g. asia), a keyring name, and a key name." + exit 1 +fi + +cat >"$noirbox_dir/config.yaml" <"$noirbox_dir/noirbox-files.txt" +fi diff --git a/bin/noirbox-register-new-file b/bin/noirbox-register-new-file new file mode 100755 index 0000000..f9ab40b --- /dev/null +++ b/bin/noirbox-register-new-file @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Adds a new file to the list of encrypted files. +# Usage: noirbox-register-new-file filename + +set -e -u -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +. "$script_dir/_noirbox-common" + +up_to_noirbox_root +read_config + +local_key="$(gcloud kms decrypt \ + --location="$location" \ + --keyring="$keyring" \ + --key="$key" \ + --plaintext-file=- \ + --ciphertext-file="$noirbox_dir/local-key.enc")" + +new_file="$1" + +while IFS="" read -r file +do + if [[ "$file" = "$new_file" ]]; then + echo "$new_file is already registered" + exit 1 + fi +done < "$noirbox_dir/noirbox-files.txt" + +sorted_files="$( (cat "$noirbox_dir/noirbox-files.txt"; echo "$new_file") | sort)" +echo "$sorted_files" > "$noirbox_dir/noirbox-files.txt" + +echo "$new_file" >> .gitignore + +encrypt_file "$new_file" "$local_key" +git add "$new_file.gpg" +git rm -f "$new_file" 2>/dev/null || true +rm -f "$new_file" + + diff --git a/bin/noirbox-rekey b/bin/noirbox-rekey new file mode 100755 index 0000000..e03895f --- /dev/null +++ b/bin/noirbox-rekey @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Creates a new key, and re-encrypts all files with the new local key. +# Usage: noirbox-rekey + +set -e -u -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +. "$script_dir/_noirbox-common" + +up_to_noirbox_root +read_config + +local_key="$(gcloud kms decrypt \ + --location="$location" \ + --keyring="$keyring" \ + --key="$key" \ + --plaintext-file=- \ + --ciphertext-file="$noirbox_dir/local-key.enc")" + +new_local_key="$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 64000)" + +while IFS="" read -r file +do + echo "rekeying $file..." + decrypt_file "$file" "$local_key" + encrypt_file "$file" "$new_local_key" +done < "$noirbox_dir/noirbox-files.txt" + +echo "$new_local_key" | gcloud kms encrypt \ + --location="$location" \ + --keyring="$keyring" \ + --key="$key" \ + --plaintext-file=- \ + --ciphertext-file="$noirbox_dir/local-key.enc" diff --git a/bin/noirbox-shred-all-files b/bin/noirbox-shred-all-files new file mode 100755 index 0000000..b275e66 --- /dev/null +++ b/bin/noirbox-shred-all-files @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Shreds all un-encrypted versions of the noirbox files +# Usage: noirbox-shred-all-files +set -e -u -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +. "$script_dir/_noirbox-common" + +up_to_noirbox_root +read_config + +while IFS="" read -r file +do + if ! shred -u "$file"; then + if ! rm -P "$file"; then + rm -f "$file" + fi + fi +done < "$noirbox_dir/noirbox-files.txt"