Imported Upstream version 2.5.1
[debian/amanda] / server-src / amcrypt-ossl-asym.sh.in
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 # change these as needed
37 OPENSSL=                        # whatever's in $PATH
38 CIPHER=aes-256-cbc              # see `openssl help` for more ciphers
39 AMANDA_HOME=~amandabackup
40 RANDFILE=$AMANDA_HOME/.rnd
41 export RANDFILE
42 PASSPHRASE=$AMANDA_HOME/.am_passphrase  # optional
43 PRIVKEY=$AMANDA_HOME/backup-privkey.pem
44 PUBKEY=$AMANDA_HOME/backup-pubkey.pem
45
46 # where might openssl be?
47 PATH=/bin:/usr/bin:/usr/local/bin:/usr/ssl/bin:/usr/local/ssl/bin
48 export PATH
49 MAGIC='AmAnDa+OpEnSsL'
50 ME=`basename "$0"`
51 WORKDIR="/tmp/.${ME}.$$"
52
53 # first things first
54 if [ -z "${OPENSSL:=`which openssl`}" ]; then
55         echo "${ME}: openssl not found" >&2
56         exit 1
57 elif [ ! -x "${OPENSSL}" ]; then
58         echo "${ME}: can't execute openssl (${OPENSSL})" >&2
59         exit 1
60 fi
61
62 if [ -n "${PASSPHRASE}" ]; then
63         # check the openssl version. if it's too old, we have to handle
64         # the pass phrase differently.
65         OSSL_VERSION=`eval \"${OPENSSL}\" version |cut -d\  -f2`
66         case "${OSSL_VERSION}" in
67          ''|0.[0-8].*|0.9.[0-6]*|0.9.7|0.9.7[a-c]*)
68                 echo "${ME}: ${OPENSSL} is version ${OSSL_VERSION}" >&2
69                 echo "${ME}: Using pass phrase kluge for OpenSSL version >=0.9.7d" >&2
70                 PASS_FROM_STDIN=yes
71                 ;;
72         esac
73 fi
74
75 mkdir -m 700 "${WORKDIR}"
76 if [ $? -ne 0 ]; then
77         echo "${ME}: failed to create temp directory" >&2
78         exit 1
79 fi
80 # ignore SIGINT
81 trap "" 2
82 trap "rm -rf \"${WORKDIR}\"" 0 1 3 15
83
84 # we'll need to pad the datastream to a multiple of the cipher block size
85 # prior to encryption and decryption. 96 bytes (= 768 bits) should be good
86 # for any cipher.
87 pad() {
88         perl -pe 'BEGIN { $bs = 96; $/ = \8192 } $nbytes = ($nbytes + length) % $bs; END { print "\0" x ($bs - $nbytes) }'
89 }
90
91 encrypt() {
92         # generate a random printable cipher key (on one line)
93         echo `"${OPENSSL}" rand -base64 80` >"${WORKDIR}/pass"
94
95         # encrypt the cipher key using the RSA public key
96         "${OPENSSL}" rsautl -encrypt -in "${WORKDIR}/pass" -out "${WORKDIR}/pass.ciphertext" -pubin -inkey "${PUBKEY}" -pkcs
97         [ $? -eq 0 ] || return 1
98
99         # print magic
100         printf %s "${MAGIC}"
101
102         # print the encrypted cipher key, preceded by size
103         ls -l "${WORKDIR}/pass.ciphertext" | awk '{ printf("%-10d", $5) }'
104         cat "${WORKDIR}/pass.ciphertext"
105
106         # encrypt data using the cipher key and print
107         pad | "${OPENSSL}" enc "-${CIPHER}" -nopad -e -pass "file:${WORKDIR}/pass" -nosalt
108         [ $? -eq 0 ] || return 1
109 }
110
111 decrypt() {
112         # read magic
113         magicsize=`printf %s "${MAGIC}" | wc -c | sed 's/^ *//'`
114         magic=`dd bs=$magicsize count=1 2>/dev/null`
115         if [ "$magic" != "${MAGIC}" ]; then
116                 echo "${ME}: bad magic" >&2
117                 return 1
118         fi
119
120         # read size of encrypted cipher key
121         n=`dd bs=10 count=1 2>/dev/null`
122         [ $n -gt 0 ] 2>/dev/null
123         if [ $? -ne 0 ]; then
124                 echo "${ME}: bad header" >&2
125                 return 1
126         fi
127
128         # read the encrypted cipher key
129         dd "of=${WORKDIR}/pass.ciphertext" bs=$n count=1 2>/dev/null
130
131         # decrypt the cipher key using the RSA private key
132         if [ "${PASS_FROM_STDIN}" = yes ]; then
133                 "${OPENSSL}" rsautl -decrypt -in "${WORKDIR}/pass.ciphertext" -out "${WORKDIR}/pass" -inkey "${PRIVKEY}" -pkcs < "${PASSPHRASE}"
134         else
135                 "${OPENSSL}" rsautl -decrypt -in "${WORKDIR}/pass.ciphertext" -out "${WORKDIR}/pass" -inkey "${PRIVKEY}" ${PASSARG} -pkcs 3< "${PASSPHRASE}"
136         fi
137         [ $? -eq 0 ] || return 1
138
139         # use the cipher key to decrypt data
140         pad | "${OPENSSL}" enc "-${CIPHER}" -nopad -d -pass "file:${WORKDIR}/pass" -nosalt
141
142         # N.B.: in the likely event that we're piping to gzip, the above command
143         # may return a spurious error if gzip closes the output stream early.
144         return 0
145 }
146
147 if [ "$1" = -d ]; then
148         if [ -z "${PRIVKEY}" ]; then
149                 echo "${ME}: must specify private key for decryption" >&2
150                 exit 1
151         elif [ ! -r "${PRIVKEY}" ]; then
152                 echo "${ME}: can't read private key from ${PRIVKEY}" >&2
153                 exit 1
154         fi
155
156         if [ -n "${PASSPHRASE}" -a -e "${PASSPHRASE}" -a -r "${PASSPHRASE}" ]; then
157                 PASSARG='-passin fd:3'
158         else
159                 PASSPHRASE=/dev/null
160         fi
161
162         decrypt
163         if [ $? -ne 0 ]; then
164                 echo "${ME}: decryption failed" >&2
165                 exit 1
166         fi
167 else
168         if [ -z "${PUBKEY}" ]; then
169                 echo "${ME}: must specify public key for encryption" >&2
170                 exit 1
171         elif [ ! -r "${PUBKEY}" ]; then
172                 echo "${ME}: can't read public key from ${PUBKEY}" >&2
173                 exit 1
174         fi
175
176         encrypt
177         if [ $? -ne 0 ]; then
178                 echo "${ME}: encryption failed" >&2
179                 exit 1
180         fi
181 fi