-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#### What I did Added support for secure upgrade #### How I did it It includes image signing during build (in sonic buildimage repo) and verification during image install (in sonic-utilities). HLD can be found in the following PR: sonic-net/SONiC#1024 #### How to verify it Feature is used to allow image was not modified since built from vendor. During installation, image can be verified with a signature attached to it. In order for image verification - image must be signed - need to provide signing key and certificate (paths in SECURE_UPGRADE_DEV_SIGNING_KEY and SECURE_UPGRADE_DEV_SIGNING_CERT in rules/config) during build , and during image install, need to enable secure boot flag in bios, and signing_certificate should be available in bios. #### Feature dependencies In order for this feature to work smoothly, need to have secure boot feature implemented as well. The Secure boot feature will be merged in the near future. sonic-buildimage PR: sonic-net/sonic-buildimage#11862
- Loading branch information
1 parent
ee53b02
commit ebc3506
Showing
12 changed files
with
408 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
#!/bin/sh | ||
image_file="${1}" | ||
cms_sig_file="sig.cms" | ||
lines_for_lookup=50 | ||
SECURE_UPGRADE_ENABLED=0 | ||
DIR="$(dirname "$0")" | ||
if [ -d "/sys/firmware/efi/efivars" ]; then | ||
if ! [ -n "$(ls -A /sys/firmware/efi/efivars 2>/dev/null)" ]; then | ||
mount -t efivarfs none /sys/firmware/efi/efivars 2>/dev/null | ||
fi | ||
SECURE_UPGRADE_ENABLED=$(bootctl status 2>/dev/null | grep -c "Secure Boot: enabled") | ||
else | ||
echo "efi not supported - exiting without verification" | ||
exit 0 | ||
fi | ||
|
||
. /usr/local/bin/verify_image_sign_common.sh | ||
|
||
if [ ${SECURE_UPGRADE_ENABLED} -eq 0 ]; then | ||
echo "secure boot not enabled - exiting without image verification" | ||
exit 0 | ||
fi | ||
|
||
clean_up () | ||
{ | ||
if [ -d ${EFI_CERTS_DIR} ]; then rm -rf ${EFI_CERTS_DIR}; fi | ||
if [ -d "${TMP_DIR}" ]; then rm -rf ${TMP_DIR}; fi | ||
exit $1 | ||
} | ||
|
||
TMP_DIR=$(mktemp -d) | ||
DATA_FILE="${TMP_DIR}/data.bin" | ||
CMS_SIG_FILE="${TMP_DIR}/${cms_sig_file}" | ||
TAR_SIZE=$(head -n $lines_for_lookup $image_file | grep "payload_image_size=" | cut -d"=" -f2- ) | ||
SHARCH_SIZE=$(sed '/^exit_marker$/q' $image_file | wc -c) | ||
SIG_PAYLOAD_SIZE=$(($TAR_SIZE + $SHARCH_SIZE )) | ||
# Extract cms signature from signed file | ||
# Add extra byte for payload | ||
sed -e '1,/^exit_marker$/d' $image_file | tail -c +$(( $TAR_SIZE + 1 )) > $CMS_SIG_FILE | ||
# Extract image from signed file | ||
head -c $SIG_PAYLOAD_SIZE $image_file > $DATA_FILE | ||
# verify signature with certificate fetched with efi tools | ||
EFI_CERTS_DIR=/tmp/efi_certs | ||
[ -d $EFI_CERTS_DIR ] && rm -rf $EFI_CERTS_DIR | ||
mkdir $EFI_CERTS_DIR | ||
efi-readvar -v db -o $EFI_CERTS_DIR/db_efi >/dev/null || | ||
{ | ||
echo "Error: unable to read certs from efi db: $?" | ||
clean_up 1 | ||
} | ||
# Convert one file to der certificates | ||
sig-list-to-certs $EFI_CERTS_DIR/db_efi $EFI_CERTS_DIR/db >/dev/null|| | ||
{ | ||
echo "Error: convert sig list to certs: $?" | ||
clean_up 1 | ||
} | ||
for file in $(ls $EFI_CERTS_DIR | grep "db-"); do | ||
LOG=$(openssl x509 -in $EFI_CERTS_DIR/$file -inform der -out $EFI_CERTS_DIR/cert.pem 2>&1) | ||
if [ $? -ne 0 ]; then | ||
logger "cms_validation: $LOG" | ||
fi | ||
# Verify detached signature | ||
LOG=$(verify_image_sign_common $image_file $DATA_FILE $CMS_SIG_FILE) | ||
VALIDATION_RES=$? | ||
if [ $VALIDATION_RES -eq 0 ]; then | ||
RESULT="CMS Verified OK using efi keys" | ||
echo "verification ok:$RESULT" | ||
# No need to continue. | ||
# Exit without error if any success signature verification. | ||
clean_up 0 | ||
fi | ||
done | ||
echo "Failure: CMS signature Verification Failed: $LOG" | ||
|
||
clean_up 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
#!/bin/bash | ||
verify_image_sign_common() { | ||
image_file="${1}" | ||
cms_sig_file="sig.cms" | ||
TMP_DIR=$(mktemp -d) | ||
DATA_FILE="${2}" | ||
CMS_SIG_FILE="${3}" | ||
|
||
openssl version | awk '$2 ~ /(^0\.)|(^1\.(0\.|1\.0))/ { exit 1 }' | ||
if [ $? -eq 0 ]; then | ||
# for version 1.1.1 and later | ||
no_check_time="-no_check_time" | ||
else | ||
# for version older than 1.1.1 use noattr | ||
no_check_time="-noattr" | ||
fi | ||
|
||
# making sure image verification is supported | ||
EFI_CERTS_DIR=/tmp/efi_certs | ||
RESULT="CMS Verification Failure" | ||
LOG=$(openssl cms -verify $no_check_time -noout -CAfile $EFI_CERTS_DIR/cert.pem -binary -in ${CMS_SIG_FILE} -content ${DATA_FILE} -inform pem 2>&1 > /dev/null ) | ||
VALIDATION_RES=$? | ||
if [ $VALIDATION_RES -eq 0 ]; then | ||
RESULT="CMS Verified OK" | ||
if [ -d "${TMP_DIR}" ]; then rm -rf ${TMP_DIR}; fi | ||
echo "verification ok:$RESULT" | ||
# No need to continue. | ||
# Exit without error if any success signature verification. | ||
return 0 | ||
fi | ||
|
||
if [ -d "${TMP_DIR}" ]; then rm -rf ${TMP_DIR}; fi | ||
return 1 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
repo_dir=$1 | ||
input_image=$2 | ||
output_file=$3 | ||
cert_file=$4 | ||
key_file=$5 | ||
tmp_dir= | ||
clean_up() | ||
{ | ||
sudo rm -rf $tmp_dir | ||
sudo rm -rf $output_file | ||
exit $1 | ||
} | ||
|
||
DIR="$(dirname "$0")" | ||
|
||
tmp_dir=$(mktemp -d) | ||
sha1=$(cat $input_image | sha1sum | awk '{print $1}') | ||
echo -n "." | ||
cp $repo_dir/installer/sharch_body.sh $output_file || { | ||
echo "Error: Problems copying sharch_body.sh" | ||
clean_up 1 | ||
} | ||
# Replace variables in the sharch template | ||
sed -i -e "s/%%IMAGE_SHA1%%/$sha1/" $output_file | ||
echo -n "." | ||
tar_size="$(wc -c < "${input_image}")" | ||
cat $input_image >> $output_file | ||
sed -i -e "s|%%PAYLOAD_IMAGE_SIZE%%|${tar_size}|" ${output_file} | ||
CMS_SIG="${tmp_dir}/signature.sig" | ||
|
||
echo "$0 CMS signing ${input_image} with ${key_file}. Output file ${output_file}" | ||
. $repo_dir/scripts/sign_image_dev.sh | ||
sign_image_dev ${cert_file} ${key_file} $output_file ${CMS_SIG} || clean_up 1 | ||
|
||
cat ${CMS_SIG} >> ${output_file} | ||
echo "Signature done." | ||
# append signature to binary | ||
sudo rm -rf ${CMS_SIG} | ||
sudo rm -rf $tmp_dir | ||
exit 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
repo_dir=$1 | ||
out_dir=$2 | ||
mock_image="mock_img.bin" | ||
output_file=$out_dir/output_file.bin | ||
cert_file=$3 | ||
other_cert_file=$4 | ||
tmp_dir= | ||
clean_up() | ||
{ | ||
sudo rm -rf $tmp_dir | ||
sudo rm -rf $mock_image | ||
exit $1 | ||
} | ||
DIR="$(dirname "$0")" | ||
[ -d $out_dir ] || rm -rf $out_dir | ||
mkdir $out_dir | ||
tmp_dir=$(mktemp -d) | ||
#generate self signed keys and certificate | ||
key_file=$tmp_dir/private-key.pem | ||
pub_key_file=$tmp_dir/public-key.pem | ||
openssl ecparam -name secp256r1 -genkey -noout -out $key_file | ||
openssl ec -in $key_file -pubout -out $pub_key_file | ||
openssl req -new -x509 -key $key_file -out $cert_file -days 360 -subj "/C=US/ST=Test/L=Test/O=Test/CN=Test" | ||
alt_key_file=$tmp_dir/alt-private-key.pem | ||
alt_pub_key_file=$tmp_dir/alt-public-key.pem | ||
openssl ecparam -name secp256r1 -genkey -noout -out $alt_key_file | ||
openssl ec -in $alt_key_file -pubout -out $alt_pub_key_file | ||
openssl req -new -x509 -key $alt_key_file -out $other_cert_file -days 360 -subj "/C=US/ST=Test/L=Test/O=Test/CN=Test" | ||
|
||
echo "this is a mock image\nThis is another line !2#4%6\n" > $mock_image | ||
echo "Created a mock image with following text:" | ||
cat $mock_image | ||
# create signed mock image | ||
|
||
sh $DIR/create_mock_image.sh $repo_dir $mock_image $output_file $cert_file $key_file || { | ||
echo "Error: unable to create mock image" | ||
clean_up 1 | ||
} | ||
|
||
[ -f "$output_file" ] || { | ||
echo "signed mock image not created - exiting without testing" | ||
clean_up 1 | ||
} | ||
|
||
test_image_1=$out_dir/test_image_1.bin | ||
cp -v $output_file $test_image_1 || { | ||
echo "Error: Problems copying image" | ||
clean_up 1 | ||
} | ||
|
||
# test_image_1 = modified image size to something else - should fail on signature verification | ||
image_size=$(sed -n 's/^payload_image_size=\(.*\)/\1/p' < $test_image_1) | ||
sed -i "/payload_image_size=/c\payload_image_size=$(($image_size - 5))" $test_image_1 | ||
|
||
test_image_2=$out_dir/test_image_2.bin | ||
cp -v $output_file $test_image_2 || { | ||
echo "Error: Problems copying image" | ||
clean_up 1 | ||
} | ||
|
||
# test_image_2 = modified image sha1 to other sha1 value - should fail on signature verification | ||
im_sha=$(sed -n 's/^payload_sha1=\(.*\)/\1/p' < $test_image_2) | ||
sed -i "/payload_sha1=/c\payload_sha1=2f1bbd5a0d411253103e688e4e66c00c94bedd40" $test_image_2 | ||
|
||
tmp_image=$tmp_dir/"tmp_image.bin" | ||
echo "this is a different image now" >> $mock_image | ||
sh $DIR/create_mock_image.sh $repo_dir $mock_image $tmp_image $cert_file $key_file || { | ||
echo "Error: unable to create mock image" | ||
clean_up 1 | ||
} | ||
# test_image_3 = original mock image with wrong signature | ||
# Extract cms signature from signed file | ||
test_image_3=$out_dir/"test_image_3.bin" | ||
tmp_sig="${tmp_dir}/tmp_sig.sig" | ||
TMP_TAR_SIZE=$(head -n 50 $tmp_image | grep "payload_image_size=" | cut -d"=" -f2- ) | ||
sed -e '1,/^exit_marker$/d' $tmp_image | tail -c +$(( $TMP_TAR_SIZE + 1 )) > $tmp_sig | ||
|
||
TAR_SIZE=$(head -n 50 $output_file | grep "payload_image_size=" | cut -d"=" -f2- ) | ||
SHARCH_SIZE=$(sed '/^exit_marker$/q' $output_file | wc -c) | ||
SIG_PAYLOAD_SIZE=$(($TAR_SIZE + $SHARCH_SIZE )) | ||
head -c $SIG_PAYLOAD_SIZE $output_file > $test_image_3 | ||
sudo rm -rf $tmp_image | ||
|
||
cat ${tmp_sig} >> ${test_image_3} | ||
|
||
# test_image_4 = modified image with original mock image signature | ||
test_image_4=$out_dir/"test_image_4.bin" | ||
head -c $SIG_PAYLOAD_SIZE $output_file > $test_image_4 | ||
echo "this is additional line" >> $test_image_4 | ||
cat ${tmp_sig} >> ${test_image_4} | ||
clean_up 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#!/bin/bash | ||
image_file="${1}" | ||
cert_path="${2}" | ||
cms_sig_file="sig.cms" | ||
TMP_DIR=$(mktemp -d) | ||
DATA_FILE="${TMP_DIR}/data.bin" | ||
CMS_SIG_FILE="${TMP_DIR}/${cms_sig_file}" | ||
lines_for_lookup=50 | ||
|
||
TAR_SIZE=$(head -n $lines_for_lookup $image_file | grep "payload_image_size=" | cut -d"=" -f2- ) | ||
SHARCH_SIZE=$(sed '/^exit_marker$/q' $image_file | wc -c) | ||
SIG_PAYLOAD_SIZE=$(($TAR_SIZE + $SHARCH_SIZE )) | ||
# Extract cms signature from signed file - exit marker marks last sharch prefix + number of image lines + 1 for next linel | ||
# Add extra byte for payload - extracting image signature from line after data file | ||
sed -e '1,/^exit_marker$/d' $image_file | tail -c +$(( $TAR_SIZE + 1 )) > $CMS_SIG_FILE | ||
# Extract image from signed file | ||
head -c $SIG_PAYLOAD_SIZE $image_file > $DATA_FILE | ||
EFI_CERTS_DIR=/tmp/efi_certs | ||
[ -d $EFI_CERTS_DIR ] && rm -rf $EFI_CERTS_DIR | ||
mkdir $EFI_CERTS_DIR | ||
cp $cert_path $EFI_CERTS_DIR/cert.pem | ||
|
||
DIR="$(dirname "$0")" | ||
. $DIR/verify_image_sign_common.sh | ||
verify_image_sign_common $image_file $DATA_FILE $CMS_SIG_FILE | ||
VERIFICATION_RES=$? | ||
if [ -d "${TMP_DIR}" ]; then rm -rf ${TMP_DIR}; fi | ||
[ -d $EFI_CERTS_DIR ] && rm -rf $EFI_CERTS_DIR | ||
exit $VERIFICATION_RES |
Oops, something went wrong.