make getpeername() return the original socket address which before it was intercepted
[hband-tools.git] / user-tools / rsacrypt
blob1029fe2485e1728e7ffeb7e61a04c95a1ad2d25e
1 #!/bin/bash
3 true <<EOF
4 =pod
6 =head1 NAME
8 rsacrypt - Encrypt/decrypt files with RSA
10 =cut
12 EOF
15 say()
17 echo "$*" >&2
19 die()
21 say "$@"
22 exit 1
24 shasum()
26 if [ $sign = yes ]
27 then
28 sha256sum -b | cut -b 1-64
31 subshell_exception()
33 say Error occured in subprocess, killing main process
34 kill $mainpid 2>/dev/null
36 set_filename()
38 if [ -z "$filename" ]
39 then
40 if [ -n "$1" -a "$1" != - ]
41 then
42 filename=$1
43 else
44 filename=/dev/stdin
46 else
47 die Give at most one file or pipe data on STDIN for ${mode}ion
52 # Default key files are the user's own RSA keys default used for ssh.
53 # Use -r and -k options to change.
54 keyfile=$HOME/.ssh/id_rsa
55 pubfile=$HOME/.ssh/id_rsa.pub
56 # Default mode is encryption.
57 mode=encrypt
58 filename=
59 # Sign encrypted data and verify signature by default.
60 sign=yes
61 signfile=
62 signbytes=
63 bs=256
64 mainpid=$$
67 while [ -n "$1" ]
69 case "$1" in
70 -h|--help)
71 say "Usage: $0 [Options] [FILE]
72 Options:
73 --encrypt, -e encrypt (default)
74 --decrypt, -d decrypt
75 --recipient, -r PUBLIC_KEY
76 --key, -k PRIVATE_KEY
77 --no-sign, -S do not sign the secret, and do not search for the signature
78 at the end of data when decrypting
79 --ignore-sign --sign-bytes, -S -b N
80 there is an N bytes long signature at the end of data, but
81 do not verify it
82 --signature-file FILE
83 write/read signature to/from a separated file
85 exit 0
87 -r|--recipient)
88 shift
89 pubfile=$1
91 -k|--key)
92 shift
93 keyfile=$1
95 -e|--encrypt)
96 mode=encrypt
98 -d|--decrypt)
99 mode=decrypt
101 -S|--no-sign|--ignore-sign)
102 sign=no
104 -b|--sign-bytes)
105 shift
106 let signbytes=$1
108 --signature-file)
109 shift
110 signfile=$1
113 shift
114 break
117 die Invalid option: $1
120 set_filename "$1"
122 esac
123 shift
124 done
126 for arg in "$@"
128 set_filename "$arg"
129 done
131 if [ -z "$filename" ]
132 then
133 filename=/dev/stdin
136 if [ $mode = encrypt -a -t 1 ]
137 then
138 die Encrypted data not written to a terminal. Use output redirection.
142 set -o nounset
143 set -o errexit -o pipefail
146 if [ -e "$pubfile" ]
147 then
148 if file -b "$pubfile" | grep -q OpenSSH
149 then
150 if [ ! -e "$pubfile.pem" ]
151 then
152 say Convert $pubfile to $pubfile.pem
153 ssh-keygen -f "$pubfile" -e -m PKCS8 >"$pubfile.pem"
155 pubfile=$pubfile.pem
157 say Using public key: $pubfile
158 else
159 if [ $mode = encrypt -o \( $mode = decrypt -a $sign = yes \) ]
160 then
161 die Public key not found: $pubfile
164 if [ -e "$keyfile" ]
165 then
166 say Using private key: $keyfile
167 else
168 if [ $mode = decrypt -p \( $mode = encrypt -a $sign = yes \) ]
169 then
170 die Private key not found: $keyfile
177 if [ $mode = encrypt ]
178 then
179 say Generating passkey
180 pass=`openssl rand -base64 32`
181 echo -n "$pass" | openssl rsautl -encrypt -inkey "$pubfile" -pubin
183 say Encrypting
184 exec 3>&1
185 hash=`tee /dev/fd/4 <"$filename" \
186 4> >(set -o errexit -o pipefail;
187 trap subshell_exception ERR;
188 openssl enc -aes-256-cbc -salt -pass pass:$pass >&3) |\
189 shasum`
191 if [ $sign = yes ]
192 then
193 say Signing
194 if [ -z "$signfile" ]
195 then
196 signfile=/dev/stdout
198 echo "$hash" |\
199 xxd -r -p |\
200 openssl rsautl -sign -inkey "$keyfile" >"$signfile"
202 elif [ $mode = decrypt ]
203 then
204 if [ -z "$signbytes" ]
205 then
206 # Find out how many byte the signature is.
207 if [ $sign = yes ]
208 then
209 if [ -z "$signfile" ]
210 then
211 # There is no separated signature file,
212 # signature is as long as recipient's key.
213 bits=`openssl rsa -text -noout -in "$pubfile" -pubin | sed -e '1s/.*(\([0-9]\+\) bit.*/\1/;q'`
214 let signbytes=bits/8
215 say Signature bytes: $signbytes
216 else
217 signbytes=0
219 else
220 signbytes=0
225 say Extracting passkey
226 pass=`head -c $bs | openssl rsautl -decrypt -inkey "$keyfile"`
228 say Decrypting
229 # Feed openssl with crypted data except the signature at the end of it,
230 # send openssl's output to main stdout (ie. fd/3 in the inner subshell),
231 # calc checksum and put it on outer subshell's stdout captured by $hashes.
232 # In parallel, feed an other openssl with data stream's end for signature
233 # verification, let stored signature also captured by $hashes.
234 exec 3>&1
235 hashes=(`tee /dev/fd/4 \
236 4> >(set -o errexit -o pipefail;
237 trap subshell_exception ERR;
238 head -c -$signbytes |\
239 openssl enc -d -aes-256-cbc -pass pass:$pass |\
240 tee /dev/fd/3 |\
241 shasum) \
242 1> >(set -o errexit -o pipefail;
243 trap subshell_exception ERR;
244 if [ $sign = yes ]
245 then
246 if [ -z "$signfile" ]
247 then
248 tail -c $signbytes
249 else
250 cat "$signfile"
251 fi |\
252 openssl rsautl -verify -inkey "$pubfile" -pubin |\
253 xxd -p -c 32
254 fi)`)
256 if [ $sign = yes ]
257 then
258 say Verify signature
259 if [ "${hashes[0]}" = "${hashes[1]}" ]
260 then
261 say Signature OK
262 else
263 die Signatures mismatch
266 }<"$filename"
269 say Done