-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathlockbox.sh
executable file
·286 lines (232 loc) · 9.17 KB
/
lockbox.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
#!/bin/bash
# setup Bash environment
set -euf -o pipefail
###############################################################################
# constants
###############################################################################
# version of this script
VERSION=1.2.0
###############################################################################
# functions
###############################################################################
# Prints script usage to stderr
# Arguments:
# None
# Returns:
# None
print_usage() {
cat <<EOF >&2
Maintain an encrypted lockbox of data, accessible only by you.
Performs fast file-by-file encryption on every file in a directory recursively.
This script should be capable of running in macOS or in Linux.
Usage: $0 (-e lockbox_dir|-d (manifest_file|lockbox_dir)) key
$0 -v
$0 -h
-e lockbox_dir
Encrypt mode. Encrypts all files in lockbox_dir, recursively.
Manifest file is written to stdout.
-d manifest_file
Decrypt mode. Decrypts all files listed in manifest_file.
This is the preferred decryption method.
-d lockbox_dir
Expert use only. Decrypts all files in lockbox_dir, recursively, even
if they were not originally encrypted. If new plaintext files were
added to lockbox_dir since it was encrypted, then this operation will
corrupt them. Please use '-d manifest_file' whenever possible.
-h
Display help information and exit.
-v
Display version information and exit.
key
Key to be used for encryption/decryption.
Must be represented as a string comprised of exactly 64 hex characters,
corresponding to a 256-bit (or 32-byte) key.
Most easily generated by included gen_key.sh.
Example 1:
Encrypt:
$0 -e /tmp/lockbox aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | tee manifest.txt
Decrypt:
$0 -d manifest.txt aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Example 2:
Encrypt:
$0 -e /tmp/lockbox \$(xxd -p test.key | tr -d '\n') | tee manifest.txt
Decrypt:
$0 -d manifest.txt \$(xxd -p test.key | tr -d '\n')
EOF
}
# Calculates IV for given file path
# Arguments:
# Absolute file path
# Returns:
# 128-bit IV, represented in hex
function get_iv_hex {
set -euf -o pipefail
FILE_PATH=$1
# MD5 is safe here because it's not being used for cryptographic reasons.
# we just need something 128 bits wide that is likely to be different for
# each input file path.
FILE_PATH_HASH_HEX=$(echo -n "$FILE_PATH" | md5sum | cut -d ' ' -f 1)
# ECB is safe here because we're implementing an ESSIV (of sorts),
# and we need access to a raw, single-block encryption. instead of
# encrypting the sector number, we encrypt a hash of the full file path.
openssl enc -aes-256-ecb -in <(echo "$FILE_PATH_HASH_HEX" | xxd -r -p) -e -K "$KEY_HASH_HEX" -nopad | xxd -p | tr -d '\n'
}
# export function for use by child processes
export -f get_iv_hex
# Encrypts or decrypts a file
# Arguments:
# Absolute file path
# Returns:
# None
function crypt {
set -euf -o pipefail
FILE_PATH=$1
if [[ "$OPERATION_SWITCH" == '-e' ]]; then
# calculate the hsah for the file, before we encrypt it
HASH_OUTPUT=$(sha512sum "$FILE_PATH")
fi
# CTR mode ensures that *crypted file remains same size as original.
# 8192 is chosen to match OpenSSL enc "bsize" buffer size.
openssl enc -aes-256-ctr -in "$FILE_PATH" "$OPERATION_SWITCH" -K "$KEY_HEX" -iv "$(get_iv_hex "$FILE_PATH")" | dd of="$FILE_PATH" bs=8192 conv=notrunc status=none
# assuming *cryption succeeded...
if [[ "$OPERATION_SWITCH" == '-e' ]]; then
# echo the hash output to stdout, enabling creation of manifest file
echo "$HASH_OUTPUT"
else
# echo the file path to stdout, so the user can view decryption progress
echo "$FILE_PATH"
fi
}
# export function for use by child processes
export -f crypt
###############################################################################
# set default options
###############################################################################
OPERATION=""
OPERATION_SWITCH=""
ROOT_DIR=""
MANIFEST_PATH=""
# reset in case getopts has been used previously in the shell
OPTIND=1
###############################################################################
# parse options
###############################################################################
while getopts ":e:d:vh" opt; do
case $opt in
e)
OPERATION="Encrypt"
OPERATION_SWITCH='-e'
if [[ -d "$OPTARG" ]]; then
ROOT_DIR="$OPTARG"
else
echo "[-] $OPTARG is an invalid directory" >&2
exit 1
fi
;;
d)
OPERATION="Decrypt"
OPERATION_SWITCH='-d'
# test for directory first
if [[ -d "$OPTARG" ]]; then
ROOT_DIR="$OPTARG"
elif [[ -r "$OPTARG" ]]; then
MANIFEST_PATH="$OPTARG"
else
echo "[-] $OPTARG is an invalid file or directory" >&2
exit 1
fi
;;
v)
echo "lockbox $VERSION"
echo "https://github.com/JElchison/lockbox"
echo "Copyright (C) 2018 Jonathan Elchison <[email protected]>"
exit 0
;;
h)
print_usage
exit 0
;;
\?)
echo "[-] Invalid option '-$OPTARG'" >&2
print_usage
exit 1
;;
:)
echo "[-] Option '-$OPTARG' requires an argument" >&2
print_usage
exit 1
;;
esac
done
shift $((OPTIND-1))
([[ "$1" = "--" ]] 2>/dev/null && shift) || true
# export variable for use by child processes
export OPERATION_SWITCH
###############################################################################
# validate arguments
###############################################################################
echo "[+] Validating arguments..." >&2
if [[ -z "$OPERATION" ]]; then
echo "[-] Either option '-e' or '-d' is required" >&2
print_usage
exit 1
fi
# require exactly 1 argument
if [[ $# -ne 1 ]]; then
echo "[-] key is a required argument" >&2
print_usage
exit 1
fi
# setup variables for arguments
KEY_HEX=$1
# verify that key is valid
echo -n "$KEY_HEX" | grep -Eq '[a-fA-F0-9]{64}' || (echo "[-] Provided key is invalid or incorrectly formatted" >&2; false)
echo -n "$KEY_HEX" | grep -Eq '[a]{64}' && (echo "[-] Provided key is only valid as example. Please use gen_key.sh." >&2; false)
# export variable for use by child processes
export KEY_HEX
###############################################################################
# test dependencies
###############################################################################
echo "[+] Testing dependencies..." >&2
if [[ ! -x $(which openssl 2>/dev/null) ]] ||
[[ ! -x $(which md5sum 2>/dev/null) ]] ||
[[ ! -x $(which sha256sum 2>/dev/null) ]] ||
[[ ! -x $(which sha512sum 2>/dev/null) ]] ||
[[ ! -x $(which cat 2>/dev/null) ]] ||
[[ ! -x $(which cut 2>/dev/null) ]] ||
[[ ! -x $(which readlink 2>/dev/null) ]] ||
[[ ! -x $(which find 2>/dev/null) ]] ||
[[ ! -x $(which grep 2>/dev/null) ]] ||
[[ ! -x $(which test 2>/dev/null) ]] ||
[[ ! -x $(which true 2>/dev/null) ]] ||
[[ ! -x $(which false 2>/dev/null) ]] ||
[[ ! -x $(which tr 2>/dev/null) ]] ||
[[ ! -x $(which dd 2>/dev/null) ]] ||
[[ ! -x $(which xxd 2>/dev/null) ]]; then
echo "[-] Dependencies unmet. Please verify that the following are installed, executable, and in the PATH: openssl, md5sum, sha256sum, sha512sum, cat, cut, readlink, find, grep, test, true, false, tr, dd, xxd" >&2
exit 1
fi
(openssl enc help 2>&1 || true) | grep -q -- '-aes-256-ctr' || (echo "[-] Installed version of OpenSSL does not expose required aes-256-ctr cipher" >&2; false)
(openssl enc help 2>&1 || true) | grep -q -- '-aes-256-ecb' || (echo "[-] Installed version of OpenSSL does not expose required aes-256-ecb cipher" >&2; false)
###############################################################################
# start operation
###############################################################################
# calculate SHA-256 of key, for ESSIV-like purposes. see get_iv_hex() above.
KEY_HASH_HEX=$(echo "$KEY_HEX" | xxd -r -p | sha256sum | cut -d ' ' -f 1)
# export variable for use by child processes
export KEY_HASH_HEX
echo "[+] ${OPERATION}ing following files..." >&2
if [[ "$OPERATION_SWITCH" == '-d' ]] && [[ -r "$MANIFEST_PATH" ]]; then
while IFS='' read -r LINE || [[ -n "$LINE" ]]; do
FILE=$(echo -n "$LINE" | sed -r 's/^[0-9a-f]{128} .(.+)$/\1/g')
crypt "$FILE"
done < "$MANIFEST_PATH"
echo "[+] Verifying decryption..." >&2
sha512sum -c "$MANIFEST_PATH" --quiet
else
find "$(readlink -f "$ROOT_DIR")" -type f -writable -exec bash -c 'crypt "$0"' {} \;
fi
###############################################################################
# report status
###############################################################################
echo "[+] Success" >&2