Imported Upstream version 2.6.0
[debian/amanda] / common-src / amcrypt-ossl-asym.sh
1 #!@SHELL@
2 #
3 # amcrypt-ossl-asym.sh - asymmetric crypto helper using OpenSSL
4 # Usage: amcrypt-ossl-asym.sh [-d]
5 #
6 # Copyright © 2006  Ben Slusky <sluskyb@paranoiacs.org>
7 #
8 # This program is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU General Public License
10 # as published by the Free Software Foundation; either version 2
11 # of the License, or (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 #
22
23 # Keys can be generated with the standard OpenSSL commands, e.g.:
24 #
25 # $ openssl genrsa -aes128 -out backup-privkey.pem 1024
26 # Generating RSA private key, 1024 bit long modulus
27 # [...]
28 # Enter pass phrase for backup-privkey.pem: <ENTER YOUR PASS PHRASE>
29 # Verifying - Enter pass phrase for backup-privkey.pem: <ENTER YOUR PASS PHRASE>
30 #
31 # $ openssl rsa -in backup-privkey.pem -pubout -out backup-pubkey.pem
32 # Enter pass phrase for backup-privkey.pem: <ENTER YOUR PASS PHRASE>
33 # Writing RSA key
34 #
35
36 prefix="@prefix@"
37 exec_prefix="@exec_prefix@"
38 sbindir="@sbindir@"
39 amlibexecdir="@amlibexecdir@"
40 . "${amlibexecdir}/amanda-sh-lib.sh"
41
42 # change these as needed
43 OPENSSL=                        # whatever's in $PATH
44 CIPHER=aes-256-cbc              # see `openssl help` for more ciphers
45 AMANDA_HOME=~@CLIENT_LOGIN@
46 RANDFILE=$AMANDA_HOME/.rnd
47 export RANDFILE
48 PASSPHRASE=$AMANDA_HOME/.am_passphrase  # optional
49 PRIVKEY=$AMANDA_HOME/backup-privkey.pem
50 PUBKEY=$AMANDA_HOME/backup-pubkey.pem
51
52 # where might openssl be?
53 PATH=/bin:/usr/bin:/usr/local/bin:/usr/ssl/bin:/usr/local/ssl/bin:/opt/csw/bin
54 export PATH
55 MAGIC='AmAnDa+OpEnSsL'
56 ME=`basename "$0"`
57 WORKDIR="/tmp/.${ME}.$$"
58
59
60 # first things first
61 if [ -z "${OPENSSL:=`which openssl`}" ]; then
62         echo `_ '%s: %s not found' "${ME}" "openssl"` >&2
63         exit 1
64 elif [ ! -x "${OPENSSL}" ]; then
65         echo `_ "%s: can't execute %s (%s)" "${ME}" "openssl" "${OPENSSL}"` >&2
66         exit 1
67 fi
68
69 if [ -n "${PASSPHRASE}" ]; then
70         # check the openssl version. if it's too old, we have to handle
71         # the pass phrase differently.
72         OSSL_VERSION=`eval \"${OPENSSL}\" version |cut -d\  -f2`
73         case "${OSSL_VERSION}" in
74          ''|0.[0-8].*|0.9.[0-6]*|0.9.7|0.9.7[a-c]*)
75                 echo `_ '%s: %s is version %s' "${ME}" "${OPENSSL}" "${OSSL_VERSION}"` >&2
76                 echo `_ '%s: Using pass phrase kluge for OpenSSL version >=0.9.7d' "${ME}"` >&2
77                 PASS_FROM_STDIN=yes
78                 ;;
79         esac
80 fi
81
82 mkdir -m 700 "${WORKDIR}"
83 if [ $? -ne 0 ]; then
84         echo `_ '%s: failed to create temp directory' "${ME}"` >&2
85         exit 1
86 fi
87 # ignore SIGINT
88 trap "" 2
89 trap "rm -rf \"${WORKDIR}\"" 0 1 3 15
90
91 # we'll need to pad the datastream to a multiple of the cipher block size
92 # prior to encryption and decryption. 96 bytes (= 768 bits) should be good
93 # for any cipher.
94 pad() {
95         perl -pe 'BEGIN { $bs = 96; $/ = \8192 } $nbytes = ($nbytes + length) % $bs; END { print "\0" x ($bs - $nbytes) }'
96 }
97
98 encrypt() {
99         # generate a random printable cipher key (on one line)
100         echo `"${OPENSSL}" rand -base64 80` >"${WORKDIR}/pass"
101
102         # encrypt the cipher key using the RSA public key
103         "${OPENSSL}" rsautl -encrypt -in "${WORKDIR}/pass" -out "${WORKDIR}/pass.ciphertext" -pubin -inkey "${PUBKEY}" -pkcs
104         [ $? -eq 0 ] || return 1
105
106         # print magic
107         printf "%s" "${MAGIC}"
108
109         # print the encrypted cipher key, preceded by size
110         ls -l "${WORKDIR}/pass.ciphertext" | awk '{ printf("%-10d", $5) }'
111         cat "${WORKDIR}/pass.ciphertext"
112
113         # encrypt data using the cipher key and print
114         pad | "${OPENSSL}" enc "-${CIPHER}" -nopad -e -pass "file:${WORKDIR}/pass" -nosalt
115         [ $? -eq 0 ] || return 1
116 }
117
118 decrypt() {
119         # read magic
120         magicsize=`printf "%s" "${MAGIC}" | wc -c | sed 's/^ *//'`
121         magic=`dd bs=$magicsize count=1 2>/dev/null`
122         if [ "$magic" != "${MAGIC}" ]; then
123                 echo `_ '%s: bad magic' "${ME}"` >&2
124                 return 1
125         fi
126
127         # read size of encrypted cipher key
128         n=`dd bs=10 count=1 2>/dev/null`
129         [ $n -gt 0 ] 2>/dev/null
130         if [ $? -ne 0 ]; then
131                 echo `_ '%s: bad header' "${ME}"` >&2
132                 return 1
133         fi
134
135         # read the encrypted cipher key
136         dd "of=${WORKDIR}/pass.ciphertext" bs=$n count=1 2>/dev/null
137
138         # decrypt the cipher key using the RSA private key
139         if [ "${PASS_FROM_STDIN}" = yes ]; then
140                 "${OPENSSL}" rsautl -decrypt -in "${WORKDIR}/pass.ciphertext" -out "${WORKDIR}/pass" -inkey "${PRIVKEY}" -pkcs < "${PASSPHRASE}"
141         else
142                 "${OPENSSL}" rsautl -decrypt -in "${WORKDIR}/pass.ciphertext" -out "${WORKDIR}/pass" -inkey "${PRIVKEY}" ${PASSARG} -pkcs 3< "${PASSPHRASE}"
143         fi
144         [ $? -eq 0 ] || return 1
145
146         # use the cipher key to decrypt data
147         pad | "${OPENSSL}" enc "-${CIPHER}" -nopad -d -pass "file:${WORKDIR}/pass" -nosalt
148
149         # N.B.: in the likely event that we're piping to gzip, the above command
150         # may return a spurious error if gzip closes the output stream early.
151         return 0
152 }
153
154 if [ "$1" = -d ]; then
155         if [ -z "${PRIVKEY}" ]; then
156                 echo `_ '%s: must specify private key for decryption' "${ME}"` >&2
157                 exit 1
158         elif [ ! -r "${PRIVKEY}" ]; then
159                 echo `_ "%s: can't read private key from %s" "${ME}" "${PRIVKEY}"` >&2
160                 exit 1
161         fi
162
163         if [ -n "${PASSPHRASE}" -a -e "${PASSPHRASE}" -a -r "${PASSPHRASE}" ]; then
164                 PASSARG='-passin fd:3'
165         else
166                 PASSPHRASE=/dev/null
167         fi
168
169         decrypt
170         if [ $? -ne 0 ]; then
171                 echo `_ '%s: decryption failed' "${ME}"` >&2
172                 exit 1
173         fi
174 else
175         if [ -z "${PUBKEY}" ]; then
176                 echo `_ '%s: must specify public key for encryption' "${ME}"` >&2
177                 exit 1
178         elif [ ! -r "${PUBKEY}" ]; then
179                 echo `_ "%s: can't read public key from %s" "${ME}" "${PUBKEY}"` >&2
180                 exit 1
181         fi
182
183         encrypt
184         if [ $? -ne 0 ]; then
185                 echo `_ '%s: encryption failed' "${ME}"` >&2
186                 exit 1
187         fi
188 fi