1#!/bin/bash
2#
3# Copyright (C) 2009 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18#
19# This script imports new versions of OpenSSL (http://openssl.org/source) into the
20# Android source tree.  To run, (1) fetch the appropriate tarball from the OpenSSL repository,
21# (2) check the gpg/pgp signature, and then (3) run:
22#   ./import_openssl.sh import openssl-*.tar.gz
23#
24# IMPORTANT: See README.android for additional details.
25
26# turn on exit on error as well as a warning when it happens
27set -e
28trap  "echo WARNING: Exiting on non-zero subprocess exit code" ERR;
29
30# Ensure consistent sorting order / tool output.
31export LANG=C
32export LC_ALL=C
33
34function die() {
35  declare -r message=$1
36
37  echo $message
38  exit 1
39}
40
41function usage() {
42  declare -r message=$1
43
44  if [ ! "$message" = "" ]; then
45    echo $message
46  fi
47  echo "Usage:"
48  echo "  ./import_openssl.sh import </path/to/openssl-*.tar.gz>"
49  echo "  ./import_openssl.sh regenerate <patch/*.patch>"
50  echo "  ./import_openssl.sh generate <patch/*.patch> </path/to/openssl-*.tar.gz>"
51  exit 1
52}
53
54function main() {
55  if [ ! -d patches ]; then
56    die "OpenSSL patch directory patches/ not found"
57  fi
58
59  if [ ! -f openssl.version ]; then
60    die "openssl.version not found"
61  fi
62
63  source openssl.version
64  if [ "$OPENSSL_VERSION" == "" ]; then
65    die "Invalid openssl.version; see README.android for more information"
66  fi
67
68  OPENSSL_DIR=openssl-$OPENSSL_VERSION
69  OPENSSL_DIR_ORIG=$OPENSSL_DIR.orig
70
71  if [ ! -f openssl.config ]; then
72    die "openssl.config not found"
73  fi
74
75  source openssl.config
76  if [ "$CONFIGURE_ARGS" == "" -o "$UNNEEDED_SOURCES" == "" -o "$NEEDED_SOURCES" == "" ]; then
77    die "Invalid openssl.config; see README.android for more information"
78  fi
79
80  declare -r command=$1
81  shift || usage "No command specified. Try import, regenerate, or generate."
82  if [ "$command" = "import" ]; then
83    declare -r tar=$1
84    shift || usage "No tar file specified."
85    import $tar
86  elif [ "$command" = "regenerate" ]; then
87    declare -r patch=$1
88    shift || usage "No patch file specified."
89    [ -d $OPENSSL_DIR ] || usage "$OPENSSL_DIR not found, did you mean to use generate?"
90    [ -d $OPENSSL_DIR_ORIG_ORIG ] || usage "$OPENSSL_DIR_ORIG not found, did you mean to use generate?"
91    regenerate $patch
92  elif [ "$command" = "generate" ]; then
93    declare -r patch=$1
94    shift || usage "No patch file specified."
95    declare -r tar=$1
96    shift || usage "No tar file specified."
97    generate $patch $tar
98  else
99    usage "Unknown command specified $command. Try import, regenerate, or generate."
100  fi
101}
102
103# Compute the name of an assembly source file generated by one of the
104# gen_asm_xxxx() functions below. The logic is the following:
105# - if "$2" is not empty, output it directly
106# - otherwise, change the file extension of $1 from .pl to .S and output
107#   it.
108# Usage: default_asm_file "$1" "$2"
109#     or default_asm_file "$@"
110#
111# $1: generator path (perl script)
112# $2: optional output file name.
113function default_asm_file () {
114  if [ "$2" ]; then
115    echo "$2"
116  else
117    echo "${1%%.pl}.S"
118  fi
119}
120
121# Generate an ARM assembly file.
122# $1: generator (perl script)
123# $2: [optional] output file name
124function gen_asm_arm () {
125  local OUT
126  OUT=$(default_asm_file "$@")
127  perl "$1" > "$OUT"
128}
129
130function gen_asm_mips () {
131  local OUT
132  OUT=$(default_asm_file "$@")
133  # The perl scripts expect to run the target compiler as $CC to determine
134  # the endianess of the target. Setting CC to true is a hack that forces the scripts
135  # to generate little endian output
136  CC=true perl "$1" o32 > "$OUT"
137}
138
139function gen_asm_x86 () {
140  local OUT
141  OUT=$(default_asm_file "$@")
142  perl "$1" elf -fPIC > "$OUT"
143}
144
145function gen_asm_x86_64 () {
146  local OUT
147  OUT=$(default_asm_file "$@")
148  perl "$1" elf "$OUT" > "$OUT"
149}
150
151
152# Filter all items in a list that match a given pattern.
153# $1: space-separated list
154# $2: egrep pattern.
155# Out: items in $1 that match $2
156function filter_by_egrep() {
157  declare -r pattern=$1
158  shift
159  echo "$@" | tr ' ' '\n' | grep -e "$pattern" | tr '\n' ' '
160}
161
162# Sort and remove duplicates in a space-separated list
163# $1: space-separated list
164# Out: new space-separated list
165function uniq_sort () {
166  echo "$@" | tr ' ' '\n' | sort -u | tr '\n' ' '
167}
168
169function print_autogenerated_header() {
170  echo "# Auto-generated - DO NOT EDIT!"
171  echo "# To regenerate, edit openssl.config, then run:"
172  echo "#     ./import_openssl.sh import /path/to/openssl-$OPENSSL_VERSION.tar.gz"
173  echo "#"
174}
175
176function generate_build_config_mk() {
177  ./Configure $CONFIGURE_ARGS
178  rm -f apps/CA.pl.bak crypto/opensslconf.h.bak
179
180  declare -r tmpfile=$(mktemp)
181  (grep -e -D Makefile | grep -v CONFIGURE_ARGS= | grep -v OPTIONS= | grep -v -e -DOPENSSL_NO_DEPRECATED) > $tmpfile
182
183  declare -r cflags=$(filter_by_egrep "^-D" $(grep -e "^CFLAG=" $tmpfile))
184  declare -r depflags=$(filter_by_egrep "^-D" $(grep -e "^DEPFLAG=" $tmpfile))
185  rm -f $tmpfile
186
187  echo "Generating $(basename $1)"
188  (
189    print_autogenerated_header
190
191    echo "openssl_cflags := \\"
192    for cflag in $cflags $depflags; do
193      echo "  $cflag \\"
194    done
195    echo ""
196  ) > $1
197}
198
199# Return the value of a computed variable name.
200# E.g.:
201#   FOO=foo
202#   BAR=bar
203#   echo $(var_value FOO_$BAR)   -> prints the value of ${FOO_bar}
204# $1: Variable name
205# Out: variable value
206var_value() {
207  # Note: don't use 'echo' here, because it's sensitive to values
208  #       that begin with an underscore (e.g. "-n")
209  eval printf \"%s\\n\" \$$1
210}
211
212# Same as var_value, but returns sorted output without duplicates.
213# $1: Variable name
214# Out: variable value (if space-separated list, sorted with no duplicates)
215var_sorted_value() {
216  uniq_sort $(var_value $1)
217}
218
219# Print the definition of a given variable in a GNU Make build file.
220# $1: Variable name (e.g. common_src_files)
221# $2+: Variable value (e.g. list of sources)
222print_vardef_in_mk() {
223  declare -r varname=$1
224  shift
225  if [ -z "$1" ]; then
226    echo "$varname :="
227  else
228    echo "$varname := \\"
229    for src; do
230      echo "  $src \\"
231    done
232  fi
233  echo ""
234}
235
236# Same as print_vardef_in_mk, but print a CFLAGS definition from
237# a list of compiler defines.
238# $1: Variable name (e.g. common_c_flags)
239# $2: List of defines (e.g. OPENSSL_NO_CAMELLIA ...)
240print_defines_in_mk() {
241  declare -r varname=$1
242  shift
243  if [ -z "$1" ]; then
244    echo "$varname :="
245  else
246    echo "$varname := \\"
247    for def; do
248    echo "  -D$def \\"
249    done
250  fi
251  echo ""
252}
253
254# Generate a configuration file like Crypto-config.mk
255# This uses variable definitions from openssl.config to build a config
256# file that can compute the list of target- and host-specific sources /
257# compiler flags for a given component.
258#
259# $1: Target file name.  (e.g. Crypto-config.mk)
260# $2: Variable prefix.   (e.g. CRYPTO)
261function generate_config_mk() {
262  declare -r output="$1"
263  declare -r prefix="$2"
264  declare -r all_archs="arm x86 x86_64 mips"
265
266  echo "Generating $(basename $output)"
267  (
268    print_autogenerated_header
269    echo \
270"# Before including this file, the local Android.mk must define the following
271# variables:
272#
273#    local_c_flags
274#    local_c_includes
275#    local_additional_dependencies
276#
277# This script will define the following variables:
278#
279#    target_c_flags
280#    target_c_includes
281#    target_src_files
282#
283#    host_c_flags
284#    host_c_includes
285#    host_src_files
286#
287
288# Ensure these are empty.
289unknown_arch_c_flags :=
290unknown_arch_src_files :=
291unknown_arch_exclude_files :=
292
293"
294    common_defines=$(var_sorted_value OPENSSL_${prefix}_DEFINES)
295    print_defines_in_mk common_c_flags $common_defines
296
297    common_sources=$(var_sorted_value OPENSSL_${prefix}_SOURCES)
298    print_vardef_in_mk common_src_files $common_sources
299
300    common_includes=$(var_sorted_value OPENSSL_${prefix}_INCLUDES)
301    print_vardef_in_mk common_c_includes $common_includes
302
303    for arch in $all_archs; do
304      arch_defines=$(var_sorted_value OPENSSL_${prefix}_DEFINES_${arch})
305      print_defines_in_mk ${arch}_c_flags $arch_defines
306
307      arch_sources=$(var_sorted_value OPENSSL_${prefix}_SOURCES_${arch})
308      print_vardef_in_mk ${arch}_src_files $arch_sources
309
310      arch_exclude_sources=$(var_sorted_value OPENSSL_${prefix}_SOURCES_EXCLUDES_${arch})
311      print_vardef_in_mk ${arch}_exclude_files $arch_exclude_sources
312
313    done
314
315    echo "\
316target_arch := \$(TARGET_ARCH)
317ifeq (\$(target_arch)-\$(TARGET_HAS_BIGENDIAN),mips-true)
318target_arch := unknown_arch
319endif
320
321target_c_flags    := \$(common_c_flags) \$(\$(target_arch)_c_flags) \$(local_c_flags)
322target_c_includes := \$(addprefix external/openssl/,\$(common_c_includes)) \$(local_c_includes)
323target_src_files  := \$(common_src_files) \$(\$(target_arch)_src_files)
324target_src_files  := \$(filter-out \$(\$(target_arch)_exclude_files), \$(target_src_files))
325
326ifeq (\$(HOST_OS)-\$(HOST_ARCH),linux-x86)
327host_arch := x86
328else
329host_arch := unknown_arch
330endif
331
332host_c_flags    := \$(common_c_flags) \$(\$(host_arch)_c_flags) \$(local_c_flags)
333host_c_includes := \$(addprefix external/openssl/,\$(common_c_includes)) \$(local_c_includes)
334host_src_files  := \$(common_src_files) \$(\$(host_arch)_src_files)
335host_src_files  := \$(filter-out \$(\$(host_arch)_exclude_files), \$(host_src_files))
336
337local_additional_dependencies += \$(LOCAL_PATH)/$(basename $output)
338"
339
340  ) > "$output"
341}
342
343function import() {
344  declare -r OPENSSL_SOURCE=$1
345
346  untar $OPENSSL_SOURCE readonly
347  applypatches $OPENSSL_DIR
348
349  cd $OPENSSL_DIR
350
351  generate_build_config_mk ../build-config.mk
352
353  cp -f LICENSE ../NOTICE
354  touch ../MODULE_LICENSE_BSD_LIKE
355
356  # Avoid checking in symlinks
357  for i in `find include/openssl -type l`; do
358    target=`readlink $i`
359    rm -f $i
360    if [ -f include/openssl/$target ]; then
361      cp include/openssl/$target $i
362    fi
363  done
364
365  # Generate arm asm
366  gen_asm_arm crypto/aes/asm/aes-armv4.pl
367  gen_asm_arm crypto/bn/asm/armv4-gf2m.pl
368  gen_asm_arm crypto/bn/asm/armv4-mont.pl
369  gen_asm_arm crypto/modes/asm/ghash-armv4.pl
370  gen_asm_arm crypto/sha/asm/sha1-armv4-large.pl
371  gen_asm_arm crypto/sha/asm/sha256-armv4.pl
372  gen_asm_arm crypto/sha/asm/sha512-armv4.pl
373
374  # Generate mips asm
375  gen_asm_mips crypto/aes/asm/aes-mips.pl
376  gen_asm_mips crypto/bn/asm/mips.pl crypto/bn/asm/bn-mips.S
377  gen_asm_mips crypto/bn/asm/mips-mont.pl
378  gen_asm_mips crypto/sha/asm/sha1-mips.pl
379  gen_asm_mips crypto/sha/asm/sha512-mips.pl crypto/sha/asm/sha256-mips.S
380
381  # Generate x86 asm
382  gen_asm_x86 crypto/x86cpuid.pl
383  gen_asm_x86 crypto/aes/asm/aes-586.pl
384  gen_asm_x86 crypto/aes/asm/vpaes-x86.pl
385  gen_asm_x86 crypto/aes/asm/aesni-x86.pl
386  gen_asm_x86 crypto/bn/asm/bn-586.pl
387  gen_asm_x86 crypto/bn/asm/co-586.pl
388  gen_asm_x86 crypto/bn/asm/x86-mont.pl
389  gen_asm_x86 crypto/bn/asm/x86-gf2m.pl
390  gen_asm_x86 crypto/modes/asm/ghash-x86.pl
391  gen_asm_x86 crypto/sha/asm/sha1-586.pl
392  gen_asm_x86 crypto/sha/asm/sha256-586.pl
393  gen_asm_x86 crypto/sha/asm/sha512-586.pl
394  gen_asm_x86 crypto/md5/asm/md5-586.pl
395  gen_asm_x86 crypto/des/asm/des-586.pl
396  gen_asm_x86 crypto/des/asm/crypt586.pl
397  gen_asm_x86 crypto/bf/asm/bf-586.pl
398
399  # Generate x86_64 asm
400  gen_asm_x86_64 crypto/x86_64cpuid.pl
401  gen_asm_x86_64 crypto/sha/asm/sha1-x86_64.pl
402  gen_asm_x86_64 crypto/sha/asm/sha512-x86_64.pl crypto/sha/asm/sha256-x86_64.S
403  gen_asm_x86_64 crypto/sha/asm/sha512-x86_64.pl
404  gen_asm_x86_64 crypto/modes/asm/ghash-x86_64.pl
405  gen_asm_x86_64 crypto/aes/asm/aesni-x86_64.pl
406  gen_asm_x86_64 crypto/aes/asm/vpaes-x86_64.pl
407  gen_asm_x86_64 crypto/aes/asm/bsaes-x86_64.pl
408  gen_asm_x86_64 crypto/aes/asm/aes-x86_64.pl
409  gen_asm_x86_64 crypto/aes/asm/aesni-sha1-x86_64.pl
410  gen_asm_x86_64 crypto/md5/asm/md5-x86_64.pl
411  gen_asm_x86_64 crypto/bn/asm/modexp512-x86_64.pl
412  gen_asm_x86_64 crypto/bn/asm/x86_64-mont.pl
413  gen_asm_x86_64 crypto/bn/asm/x86_64-gf2m.pl
414  gen_asm_x86_64 crypto/bn/asm/x86_64-mont5.pl
415  gen_asm_x86_64 crypto/rc4/asm/rc4-x86_64.pl
416  gen_asm_x86_64 crypto/rc4/asm/rc4-md5-x86_64.pl
417
418  # Setup android.testssl directory
419  mkdir android.testssl
420  cat test/testssl | \
421    sed 's#../util/shlib_wrap.sh ./ssltest#adb shell /system/bin/ssltest#' | \
422    sed 's#../util/shlib_wrap.sh ../apps/openssl#adb shell /system/bin/openssl#' | \
423    sed 's#adb shell /system/bin/openssl no-dh#[ `adb shell /system/bin/openssl no-dh` = no-dh ]#' | \
424    sed 's#adb shell /system/bin/openssl no-rsa#[ `adb shell /system/bin/openssl no-rsa` = no-dh ]#' | \
425    sed 's#../apps/server2.pem#/sdcard/android.testssl/server2.pem#' | \
426    cat > \
427    android.testssl/testssl
428  chmod +x android.testssl/testssl
429  cat test/Uss.cnf | sed 's#./.rnd#/sdcard/android.testssl/.rnd#' >> android.testssl/Uss.cnf
430  cat test/CAss.cnf | sed 's#./.rnd#/sdcard/android.testssl/.rnd#' >> android.testssl/CAss.cnf
431  cp apps/server2.pem android.testssl/
432  cp ../patches/testssl.sh android.testssl/
433
434  cd ..
435
436  generate_config_mk Crypto-config.mk CRYPTO
437  generate_config_mk Ssl-config.mk SSL
438  generate_config_mk Apps-config.mk APPS
439
440  # Prune unnecessary sources
441  prune
442
443  NEEDED_SOURCES="$NEEDED_SOURCES android.testssl"
444  for i in $NEEDED_SOURCES; do
445    echo "Updating $i"
446    rm -r $i
447    mv $OPENSSL_DIR/$i .
448  done
449
450  cleantar
451}
452
453function regenerate() {
454  declare -r patch=$1
455
456  generatepatch $patch
457}
458
459function generate() {
460  declare -r patch=$1
461  declare -r OPENSSL_SOURCE=$2
462
463  untar $OPENSSL_SOURCE
464  applypatches $OPENSSL_DIR_ORIG $patch
465  prune
466
467  for i in $NEEDED_SOURCES; do
468    echo "Restoring $i"
469    rm -r $OPENSSL_DIR/$i
470    cp -rf $i $OPENSSL_DIR/$i
471  done
472
473  generatepatch $patch
474  cleantar
475}
476
477# Find all files in a sub-directory that are encoded in ISO-8859
478# $1: Directory.
479# Out: list of files in $1 that are encoded as ISO-8859.
480function find_iso8859_files() {
481  find $1 -type f -print0 | xargs -0 file | fgrep "ISO-8859" | cut -d: -f1
482}
483
484# Convert all ISO-8859 files in a given subdirectory to UTF-8
485# $1: Directory name
486function convert_iso8859_to_utf8() {
487  declare -r iso_files=$(find_iso8859_files "$1")
488  for iso_file in $iso_files; do
489    iconv --from-code iso-8859-1 --to-code utf-8 $iso_file > $iso_file.tmp
490    rm -f $iso_file
491    mv $iso_file.tmp $iso_file
492  done
493}
494
495function untar() {
496  declare -r OPENSSL_SOURCE=$1
497  declare -r readonly=$2
498
499  # Remove old source
500  cleantar
501
502  # Process new source
503  tar -zxf $OPENSSL_SOURCE
504  convert_iso8859_to_utf8 $OPENSSL_DIR
505  cp -rfP $OPENSSL_DIR $OPENSSL_DIR_ORIG
506  if [ ! -z $readonly ]; then
507    find $OPENSSL_DIR_ORIG -type f -print0 | xargs -0 chmod a-w
508  fi
509}
510
511function prune() {
512  echo "Removing $UNNEEDED_SOURCES"
513  (cd $OPENSSL_DIR_ORIG && rm -rf $UNNEEDED_SOURCES)
514  (cd $OPENSSL_DIR      && rm -r  $UNNEEDED_SOURCES)
515}
516
517function cleantar() {
518  rm -rf $OPENSSL_DIR_ORIG
519  rm -rf $OPENSSL_DIR
520}
521
522function applypatches () {
523  declare -r dir=$1
524  declare -r skip_patch=$2
525
526  cd $dir
527
528  # Apply appropriate patches
529  for i in $OPENSSL_PATCHES; do
530    if [ ! "$skip_patch" = "patches/$i" ]; then
531      echo "Applying patch $i"
532      patch -p1 --merge < ../patches/$i || die "Could not apply patches/$i. Fix source and run: $0 regenerate patches/$i"
533    else
534      echo "Skiping patch $i"
535    fi
536
537  done
538
539  # Cleanup patch output
540  find . \( -type f -o -type l \) -name "*.orig" -print0 | xargs -0 rm -f
541
542  cd ..
543}
544
545function generatepatch() {
546  declare -r patch=$1
547
548  # Cleanup stray files before generating patch
549  find $BOUNCYCASTLE_DIR -type f -name "*.orig" -print0 | xargs -0 rm -f
550  find $BOUNCYCASTLE_DIR -type f -name "*~" -print0 | xargs -0 rm -f
551
552  declare -r variable_name=OPENSSL_PATCHES_`basename $patch .patch | sed s/-/_/`_SOURCES
553  # http://tldp.org/LDP/abs/html/ivr.html
554  eval declare -r sources=\$$variable_name
555  rm -f $patch
556  touch $patch
557  for i in $sources; do
558    LC_ALL=C TZ=UTC0 diff -aup $OPENSSL_DIR_ORIG/$i $OPENSSL_DIR/$i >> $patch && die "ERROR: No diff for patch $path in file $i"
559  done
560  echo "Generated patch $patch"
561  echo "NOTE To make sure there are not unwanted changes from conflicting patches, be sure to review the generated patch."
562}
563
564main $@
565