blob: 7ffe4582248697c67812f39df9d858d6f40cc6af [file] [log] [blame] [edit]
#!/bin/bash -e
# Copyright (c) 2020 Amlogic, Inc. All rights reserved.
#
# This source code is subject to the terms and conditions defined in the
# file 'LICENSE' which is part of this source code package.
#set -x
SCRIPT_PATH=${SCRIPT_PATH:-$(dirname $(readlink -f $0))}
# Temporary files directory
if [ "$TMP" == "/tmp" ] || [ -z "$TMP" ]; then
TMP=${SCRIPT_PATH}/tmp
fi
trace ()
{
echo ">>> $@" > /dev/null
}
usage() {
cat << EOF
Usage: $(basename $0) --help
Amlogic Device Vendor Secure Chipset Startup (SCS) Signing
$(basename $0) --sign-kernel \\
--key-dir <key-dir> \\
--project <project-name> \\
--input <input img> \\
{--rootkey-index [0 | 1 | 2 | 3]} \\
--output <output img>
EOF
exit 1
}
check_file() {
if [ ! -f "$2" ]; then echo Error: Unable to open $1: \""$2"\"; exit 1 ; fi
}
check_dir() {
if [ ! -d "$1" ]; then echo "Error: directory \""$1"\" does NOT exist"; usage ; fi
}
check_value() {
local val=$1
local begin=$2
local end=$3
if [ $val -lt $begin ] || [ $val -gt $end ]; then
echo "Error: Value $val is not in range [$begin, $end]"
exit 1
fi
}
# Calculate aligned file size
# $1: file
# $2: alignment requirement in bytes
aligned_size() {
local file=$1
local skip=$2
local alignment=$3
local alignedsize=0
local filesize=$(wc -c < ${file})
#echo "Input $file filesize $filesize"
if [ $skip -ne 0 ]; then
filesize=$(( $filesize - $skip ))
fi
local rem=$(( $filesize % $alignment ))
if [ $rem -ne 0 ]; then
#echo "Input $file not $alignment byte aligned"
local padsize=$(( $alignment - $rem ))
alignedsize=$(( $filesize + $padsize ))
else
alignedsize=$filesize
fi
#echo "Aligned size $alignedsize"
echo $alignedsize
}
# Pad file to len by adding 0's to end of file
# $1: file
# $2: len
pad_file() {
local file=$1
local len=$2
if [ ! -f "$1" ] || [ -z "$2" ]; then
echo "Argument error, \"$1\", \"$2\" "
exit 1
fi
local filesize=$(wc -c < ${file})
local padlen=$(( $len - $filesize ))
if [ $len -lt $filesize ]; then
echo "File larger than expected. $filesize, $len"
exit 1
fi
dd if=/dev/zero of=$file oflag=append conv=notrunc bs=1 \
count=$padlen >& /dev/null
}
append_uint32_le() {
local input=$1
local output=$2
local v=
local vrev=
v=$(printf %08x $input)
# 00010001
vrev=${v:6:2}${v:4:2}${v:2:2}${v:0:2}
echo $vrev | xxd -r -p >> $output
}
# $1: input
# $2: output
# $3: aes key file
# $4: aes iv file
internal_encrypt() {
local input=$1
local output=$2
local keyfile=$3
local ivfile=$4
if [ ! -f "$1" ] || [ -z "$2" ] || [ -z "$3" ] || [ -z "$4" ]; then
echo "Argument error"
exit 1
fi
local key=$(xxd -p -c64 $keyfile)
local iv=$(xxd -p -c64 $ivfile)
local imagesize=$(wc -c < ${input})
local rem=$(( $imagesize % 16 ))
if [ $rem -ne 0 ]; then
echo "Input $input not 16 byte aligned?"
exit 1
fi
openssl enc -aes-256-cbc -K $key -iv $iv -e -in $input -out $output -nopad
}
# Check input is android format or not
is_android_img() {
local input=$1
if [ ! -f "$1" ]; then
echo "Argument error, \"$1\""
exit 1
fi
local insize=$(wc -c < $input)
if [ $insize -le 2048 ]; then
# less than size of img header
echo False
return
fi
local inmagic=$(xxd -p -l 8 $input)
if [ "$inmagic" == "414e44524f494421" ]; then
echo True
else
echo False
fi
}
# Check input is android R format or not
# 1: input
# returns True or False
# android R file format: 4KB header + kernel/ramdisk/dtb
# file header as following
#define ANDROID_R_IMG_VER (3)
#typedef struct {
# char magic[ANDR_BOOT_MAGIC_SIZE]; /*"ANDROID!"*/
#
# u32 kernel_size; /* size in bytes */
# u32 ramdisk_size; /* size in bytes */
#
# /* Operating system version and security patch level.
# For version "A.B.C" and patch level "Y-M-D":
# (7 bits for each of A, B, C; 7 bits for (Y-2000), 4 bits for M)
# os_version = A[31:25] B[24:18] C[17:11] (Y-2000)[10:4] M[3:0]
# */
#
# uint32_t os_version;
# uint32_t header_size;
# uint32_t reserved[4];
#
# uint32_t header_version; /* Version of the boot image header */
# char cmdline[BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE];
# unsigned char szReserved[BOOT_IMG_V3_HDR_SIZE - 1580]; /*align to 4KB header,1580 is size before this*/
#}boot_img_hdr_v3_t, * p_boot_img_hdr_v3_t;
is_androidR_img() {
local input=$1
if [ ! -f "$1" ]; then
echo "Argument error, \"$1\""
exit 1
fi
local insize=$(wc -c < $input)
if [ $insize -le 4096 ]; then
# less than size of img header
echo False
return
fi
local inmagic=$(xxd -p -l 8 $input)
if [ "$inmagic" == "414e44524f494421" ]; then
inversion=$(xxd -p -seek 40 -l 4 $input)
if [ "$inversion" == "03000000" ]; then
echo True
else
echo False
fi
elif [ "$inmagic" == "564e4452424f4f54" ]; then
echo True
else
echo False
fi
}
# Encrypt/sign kernel
#typedef struct {
# uint32_t magic;
# uint32_t version;
# uint32_t flags;
# uint32_t img_version;
# uint32_t img_size;
# uint32_t img_offset;
# uint8_t img_hash[32];
# uint8_t reserved[200];
# uint8_t aesblk_sig[512];
# uint8_t rsa_sig[512];
#} aml_boot_header_t;
#
#CASSERT(sizeof(aml_boot_header_t) == 1280, assert_sizeof_aml_boot_header_t);
sign_kernel() {
local input=""
local key_dir=""
local part=""
local rootkey_index=0
local output=""
local rsakey=""
local aeskey=""
local aesiv=$TMP/aesiv.bin
local argv=("$@")
local i=0
# Parse args
i=0
while [ $i -lt $# ]; do
arg="${argv[$i]}"
i=$((i + 1))
case "$arg" in
--key-dir)
key_dir="${argv[$i]}"
check_dir "${key_dir}"
;;
--project)
part="${argv[$i]}"
;;
--input)
input="${argv[$i]}"
;;
--rootkey-index)
rootkey_index="${argv[$i]}"
check_value "${rootkey_index}" 0 3
;;
--output)
output="${argv[$i]}"
;;
*)
echo "Unknown option $arg"; exit 1
;;
esac
i=$((i + 1))
done
# Verify args
if [ -z "${key_dir}" ]; then
usage
fi
if [ -z "${part}" ]; then
echo "Error: project cannot be empty"
usage
fi
if [ -z "${rootkey_index}" ]; then
rootkey_index=0
fi
check_file "input" "${input}"
if [ -z "${output}" ]; then
echo Error: Missing output file option --output; exit 1;
fi
rsakey=$(readlink -f ${key_dir})/fip/rsa/${part}/rootrsa-${rootkey_index}/key/krnl-level-3-rsa-priv.pem
check_file "RSA key" "${rsakey}"
aeskey=$(readlink -f ${key_dir})/fip/aes/${part}/protkey/genkey-prot-krnl.bin
check_file "AES key" "${aeskey}"
dd if=/dev/zero of=${aesiv} bs=1 count=16 >& /dev/null
check_file "AES IV" "${aesiv}"
local imagesize=$(wc -c < ${input})
local rem=$(( $imagesize % 512 ))
if [ $rem -ne 0 ]; then
#echo "Input $input not 512 byte aligned?"
local topad=$(( 512 - $rem ))
imagesize=$(( $imagesize + $topad ))
cp $input $TMP/kernpad.bin
pad_file $TMP/kernpad.bin $imagesize
input=$TMP/kernpad.bin
fi
# Hash payload
openssl dgst -sha256 -binary $input > $TMP/kern-pl.sha
# Encrypt payload
internal_encrypt $input $TMP/kern-pl.bin $aeskey $aesiv
# Create header
# magic, version, flags, img_version
echo -n '@AML' > $TMP/kern.hdr
append_uint32_le 1 $TMP/kern.hdr
append_uint32_le 0 $TMP/kern.hdr
append_uint32_le 0 $TMP/kern.hdr
# img_size, img_offset, img_hash, reserved
append_uint32_le $imagesize $TMP/kern.hdr
append_uint32_le 768 $TMP/kern.hdr
cat $TMP/kern-pl.sha >> $TMP/kern.hdr
pad_file $TMP/kern.hdr 256
# Sign header
openssl dgst -sha256 -sign $rsakey -out $TMP/kern.hdr.sig $TMP/kern.hdr
# Combine header + signature
cat $TMP/kern.hdr.sig >> $TMP/kern.hdr
# Pad to 768 in case key/sig is smaller than maximum
pad_file $TMP/kern.hdr 768
# Combine hdr + payload
cat $TMP/kern.hdr $TMP/kern-pl.bin > $output
#......
#android boot/vendorboot special process
if [ "$(is_androidR_img ${input})" == "True" ]; then
local tempfile=${output}.`date +%Y%m%d%H%M%S`
dd if=${input} of=${tempfile} bs=512 count=8 &> /dev/null
cat ${output} >> ${tempfile}
mv -f ${tempfile} ${output}
elif [ "$(is_android_img ${input})" == "True" ]; then
local tempfile=${output}.`date +%Y%m%d%H%M%S`
dd if=${input} of=${tempfile} bs=512 count=4 &> /dev/null
dd if=/dev/zero of=${tempfile} bs=512 count=4 oflag=append conv=notrunc &> /dev/null
cat ${output} >> ${tempfile}
mv -f ${tempfile} ${output}
fi
echo
echo Created signed kernel $output successfully
}
parse_main() {
local i=0
local argv=()
for arg in "$@" ; do
argv[$i]="$arg"
i=$((i + 1))
done
i=0
while [ $i -lt $# ]; do
arg="${argv[$i]}"
case "$arg" in
-h|--help)
usage
break ;;
--sign-kernel)
sign_kernel "${argv[@]:$((i + 1))}"
break ;;
*)
echo "Unknown first option $1"; exit 1
;;
esac
i=$((i + 1))
done
}
cleanup() {
if [ ! -d "$TMP" ]; then return; fi
local tmpfiles="kernpad.bin kern.hdr kern.hdr.sig kern-pl.sha kern-pl.bin aesiv.bin"
for i in $tmpfiles ; do
rm -f $TMP/$i
done
rm -fr $TMP
}
trap cleanup EXIT
cleanup
if [ ! -d "$TMP" ]; then mkdir "$TMP" ; fi
parse_main "$@"