diff options
author | Nicolas Norvez <norvez@chromium.org> | 2018-02-12 14:04:03 -0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-02-28 21:29:46 -0800 |
commit | c161c4f43a71a19a97009de698654aff8984629e (patch) | |
tree | cb05bd66ef499f4b8eb03c35bd54ac5e967c44cf /scripts | |
parent | 7efa7465b17adc361c00e4e7e92f74a256038d52 (diff) | |
download | vboot-c161c4f43a71a19a97009de698654aff8984629e.tar.gz |
image_signing: unit tests for Android image
- move helper functions that detect which keys should be used depending
on the build flavor to a separate lib
- add unit tests for that lib
BUG=b:72947583
TEST=unit tests
TEST=run against caroline image, scripts detects 'cheets' build flavor
TEST=run against novato-arc64 image (SDK), script detects 'cheets' build
flavor
TEST=run against newbie image (AOSP), script detects 'aosp' build flavor
TEST=run against invalid build property 'paosp_cheets_...', script
aborts as expected
BRANCH=None
Change-Id: I5595c10a5a063e7658d0cf17c77dbeead429cd97
Reviewed-on: https://chromium-review.googlesource.com/923097
Commit-Ready: Nicolas Norvez <norvez@chromium.org>
Tested-by: Nicolas Norvez <norvez@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/image_signing/lib/sign_android_lib.sh | 133 | ||||
-rwxr-xr-x | scripts/image_signing/sign_android_image.sh | 84 | ||||
-rwxr-xr-x | scripts/image_signing/sign_android_unittests.sh | 210 |
3 files changed, 351 insertions, 76 deletions
diff --git a/scripts/image_signing/lib/sign_android_lib.sh b/scripts/image_signing/lib/sign_android_lib.sh new file mode 100644 index 00000000..eae2a360 --- /dev/null +++ b/scripts/image_signing/lib/sign_android_lib.sh @@ -0,0 +1,133 @@ +#!/bin/bash + +# Copyright 2018 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +. "$(dirname "$0")/common.sh" + +####################################### +# Return name according to the current signing debug key. The name is used to +# select key files. +# Globals: +# None +# Arguments: +# sha1: signature of the APK. +# keyset: "cheets" or "aosp" build? +# Outputs: +# Writes the name of the key to stdout. +# Returns: +# 0 on success, non-zero on error. +####################################### +android_choose_key() { + local sha1="$1" + local keyset="$2" + + if [[ "${keyset}" != "aosp" && "${keyset}" != "cheets" ]]; then + error "Unknown Android build keyset '${keyset}'." + return 1 + fi + + # Fingerprints below are generated by: + # 'cheets' keyset: + # $ keytool -file vendor/google/certs/cheetskeys/$NAME.x509.pem -printcert \ + # | grep SHA1: + # 'aosp' keyset: + # $ keytool -file build/target/product/security/$NAME.x509.pem -printcert \ + # | grep SHA1: + declare -A platform_sha=( + ['cheets']='AA:04:E0:5F:82:9C:7E:D1:B9:F8:FC:99:6C:5A:54:43:83:D9:F5:BC' + ['aosp']='27:19:6E:38:6B:87:5E:76:AD:F7:00:E7:EA:84:E4:C6:EE:E3:3D:FA' + ) + declare -A media_sha=( + ['cheets']='D4:C4:2D:E0:B9:1B:15:72:FA:7D:A7:21:E0:A6:09:94:B4:4C:B5:AE' + ['aosp']='B7:9D:F4:A8:2E:90:B5:7E:A7:65:25:AB:70:37:AB:23:8A:42:F5:D3' + ) + declare -A shared_sha=( + ['cheets']='38:B6:2C:E1:75:98:E3:E1:1C:CC:F6:6B:83:BB:97:0E:2D:40:6C:AE' + ['aosp']='5B:36:8C:FF:2D:A2:68:69:96:BC:95:EA:C1:90:EA:A4:F5:63:0F:E5' + ) + declare -A release_sha=( + ['cheets']='EC:63:36:20:23:B7:CB:66:18:70:D3:39:3C:A9:AE:7E:EF:A9:32:42' + ['aosp']='61:ED:37:7E:85:D3:86:A8:DF:EE:6B:86:4B:D8:5B:0B:FA:A5:AF:81' + ) + + case "${sha1}" in + "${platform_sha["${keyset}"]}") + echo "platform" + ;; + "${media_sha["${keyset}"]}") + echo "media" + ;; + "${shared_sha["${keyset}"]}") + echo "shared" + ;; + "${release_sha["${keyset}"]}") + # The release_sha[] fingerprint is from devkey. Translate to releasekey. + echo "releasekey" + ;; + *) + # Not a framework apk. Do not re-sign. + echo "" + ;; + esac + return 0 +} + +####################################### +# Extract 'ro.build.flavor' property from build property file. +# Globals: +# None +# Arguments: +# build_prop_file: path to build property file. +# Outputs: +# Writes the value of the property to stdout. +# Returns: +# 0 on success, non-zero on error. +####################################### +android_get_build_flavor_prop() { + local build_prop_file="$1" + local flavor_prop="" + + if ! flavor_prop=$(grep -a "^ro\.build\.flavor=" "${build_prop_file}"); then + return 1 + fi + flavor_prop=$(echo "${flavor_prop}" | cut -d "=" -f2) + echo "${flavor_prop}" + return 0 +} + +####################################### +# Pick the expected keyset ('cheets', 'aosp') depending on the build flavor. +# Globals: +# None +# Arguments: +# flavor_prop: the value of the build flavor property. +# Outputs: +# Writes the name of the keyset to stdout. +# Returns: +# 0 on success, non-zero on error. +####################################### +android_choose_signing_keyset() { + local flavor_prop="$1" + + # Property ro.build.flavor follows those patterns: + # - cheets builds: + # ro.build.flavor=cheets_${arch}-user(debug) + # - SDK builds: + # ro.build.flavor=sdk_google_cheets_${arch}-user(debug) + # - AOSP builds: + # ro.build.flavor=aosp_cheets_${arch}-user(debug) + # "cheets" and "SDK" builds both use the same signing keys, cheetskeys. "AOSP" + # builds use the public AOSP signing keys. + if [[ "${flavor_prop}" == aosp_cheets_* ]]; then + keyset="aosp" + elif [[ "${flavor_prop}" == cheets_* || + "${flavor_prop}" == sdk_google_cheets_* ]]; then + keyset="cheets" + else + return 1 + fi + echo "${keyset}" + return 0 +} diff --git a/scripts/image_signing/sign_android_image.sh b/scripts/image_signing/sign_android_image.sh index a205b5ae..5a6d321a 100755 --- a/scripts/image_signing/sign_android_image.sh +++ b/scripts/image_signing/sign_android_image.sh @@ -5,6 +5,7 @@ # found in the LICENSE file. . "$(dirname "$0")/common.sh" +. "$(dirname "$0")/lib/sign_android_lib.sh" set -e @@ -31,63 +32,6 @@ EOF exit 0 } -# Return name according to the current signing debug key. The name is used to -# select key files. -choose_key() { - local sha1="$1" - local keyset="$2" - - if [[ "${keyset}" != "aosp" && "${keyset}" != "cheets" ]]; then - error "Unknown Android build keyset '${keyset}'" - return 1 - fi - - # Fingerprints below are generated by: - # 'cheets' keyset: - # $ keytool -file vendor/google/certs/cheetskeys/$NAME.x509.pem -printcert \ - # | grep SHA1: - # 'aosp' keyset: - # $ keytool -file build/target/product/security/$NAME.x509.pem -printcert \ - # | grep SHA1: - declare -A platform_sha=( - ['cheets']='AA:04:E0:5F:82:9C:7E:D1:B9:F8:FC:99:6C:5A:54:43:83:D9:F5:BC' - ['aosp']='27:19:6E:38:6B:87:5E:76:AD:F7:00:E7:EA:84:E4:C6:EE:E3:3D:FA' - ) - declare -A media_sha=( - ['cheets']='D4:C4:2D:E0:B9:1B:15:72:FA:7D:A7:21:E0:A6:09:94:B4:4C:B5:AE' - ['aosp']='B7:9D:F4:A8:2E:90:B5:7E:A7:65:25:AB:70:37:AB:23:8A:42:F5:D3' - ) - declare -A shared_sha=( - ['cheets']='38:B6:2C:E1:75:98:E3:E1:1C:CC:F6:6B:83:BB:97:0E:2D:40:6C:AE' - ['aosp']='5B:36:8C:FF:2D:A2:68:69:96:BC:95:EA:C1:90:EA:A4:F5:63:0F:E5' - ) - declare -A release_sha=( - ['cheets']='EC:63:36:20:23:B7:CB:66:18:70:D3:39:3C:A9:AE:7E:EF:A9:32:42' - ['aosp']='61:ED:37:7E:85:D3:86:A8:DF:EE:6B:86:4B:D8:5B:0B:FA:A5:AF:81' - ) - - case "${sha1}" in - "${platform_sha["${keyset}"]}") - echo "platform" - ;; - "${media_sha["${keyset}"]}") - echo "media" - ;; - "${shared_sha["${keyset}"]}") - echo "shared" - ;; - "${release_sha["${keyset}"]}") - # The release_sha[] fingerprint is from devkey. Translate to releasekey. - echo "releasekey" - ;; - *) - # Not a framework apk. Do not re-sign. - echo "" - ;; - esac - return 0 -} - # Re-sign framework apks with the corresponding release keys. Only apk with # known key fingerprint are re-signed. We should not re-sign non-framework # apks. @@ -97,25 +41,13 @@ sign_framework_apks() { local flavor_prop="" local keyset="" - # Property ro.build.flavor follows those patterns: - # - cheets builds: - # ro.build.flavor=cheets_${arch}-user(debug) - # - SDK builds: - # ro.build.flavor=sdk_google_cheets_${arch}-user(debug) - # - AOSP builds: - # ro.build.flavor=aosp_cheets_${arch}-user(debug) - # "cheets" and "SDK" builds both use the same signing keys, cheetskeys. "AOSP" - # builds use the public AOSP signing keys. - flavor_prop=$(grep -a "^ro\.build\.flavor=" \ - "${system_mnt}/system/build.prop" | cut -d "=" -f2) - + if ! flavor_prop=$(android_get_build_flavor_prop \ + "${system_mnt}/system/build.prop"); then + die "Failed to extract build flavor property from \ +'${system_mnt}/system/build.prop'." + fi info "Found build flavor property '${flavor_prop}'." - if [[ "${flavor_prop}" == aosp_cheets_* ]]; then - keyset="aosp" - elif [[ "${flavor_prop}" == cheets_* || - "${flavor_prop}" == sdk_google_cheets_* ]]; then - keyset="cheets" - else + if ! keyset=$(android_choose_signing_keyset "${flavor_prop}"); then die "Unknown build flavor property '${flavor_prop}'." fi info "Expecting signing keyset '${keyset}'." @@ -137,7 +69,7 @@ sign_framework_apks() { sha1=$(unzip -p "${apk}" META-INF/CERT.RSA | \ keytool -printcert | awk '/^\s*SHA1:/ {print $2}') - if ! keyname=$(choose_key "${sha1}" "${keyset}"); then + if ! keyname=$(android_choose_key "${sha1}" "${keyset}"); then die "Failed to choose signing key for APK '${apk}' (SHA1 '${sha1}') in \ build flavor '${flavor_prop}'." fi diff --git a/scripts/image_signing/sign_android_unittests.sh b/scripts/image_signing/sign_android_unittests.sh new file mode 100755 index 00000000..00a651ff --- /dev/null +++ b/scripts/image_signing/sign_android_unittests.sh @@ -0,0 +1,210 @@ +#!/bin/bash + +# Copyright 2018 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +. "$(dirname "$0")/lib/sign_android_lib.sh" + +# Expected APK signatures depending on the type of APK and the type of build. +declare -A platform_sha=( + ['cheets']='AA:04:E0:5F:82:9C:7E:D1:B9:F8:FC:99:6C:5A:54:43:83:D9:F5:BC' + ['aosp']='27:19:6E:38:6B:87:5E:76:AD:F7:00:E7:EA:84:E4:C6:EE:E3:3D:FA' +) +declare -A media_sha=( + ['cheets']='D4:C4:2D:E0:B9:1B:15:72:FA:7D:A7:21:E0:A6:09:94:B4:4C:B5:AE' + ['aosp']='B7:9D:F4:A8:2E:90:B5:7E:A7:65:25:AB:70:37:AB:23:8A:42:F5:D3' +) +declare -A shared_sha=( + ['cheets']='38:B6:2C:E1:75:98:E3:E1:1C:CC:F6:6B:83:BB:97:0E:2D:40:6C:AE' + ['aosp']='5B:36:8C:FF:2D:A2:68:69:96:BC:95:EA:C1:90:EA:A4:F5:63:0F:E5' +) +declare -A release_sha=( + ['cheets']='EC:63:36:20:23:B7:CB:66:18:70:D3:39:3C:A9:AE:7E:EF:A9:32:42' + ['aosp']='61:ED:37:7E:85:D3:86:A8:DF:EE:6B:86:4B:D8:5B:0B:FA:A5:AF:81' +) + + +test_android_choose_key_invalid_keyset() { + local keyname + local keyset + local keysets=("invalid_keyset" " " "") + + for keyset in "${keysets[@]}"; do + echo "TEST: Detection of invalid keyset '${keyset}'." + if keyname=$(android_choose_key "ignored_sha1" "${keyset}"); then + : $(( NUM_TEST_FAILURES += 1 )) + echo "ERROR: Failed to detect invalid keyset '${keyset}'." + else + echo "PASS: Detected invalid keyset '${keyset}'." + fi + done +} + +android_choose_key_test_helper() { + local sha1="$1" + local keyset="$2" + local expected_keyname="$3" + local keyname="invalid_key" + + echo "TEST: Detect '${expected_keyname}' key name for '${keyset}' keyset." + keyname=$(android_choose_key "${sha1}" "${keyset}") + if [[ "${keyname}" != "${expected_keyname}" ]]; then + : $(( NUM_TEST_FAILURES += 1 )) + echo "ERROR: Incorrect key name '${keyname}' returned." + else + echo "PASS: Correct key name '${keyname}' returned." + fi +} + +test_android_choose_key() { + local keyset + local expected_keyname + + local keysets=("cheets" "aosp") + for keyset in "${keysets[@]}"; do + expected_keyname="platform" + android_choose_key_test_helper "${platform_sha[${keyset}]}" "${keyset}" \ + "${expected_keyname}" + expected_keyname="media" + android_choose_key_test_helper "${media_sha[${keyset}]}" "${keyset}" \ + "${expected_keyname}" + expected_keyname="shared" + android_choose_key_test_helper "${shared_sha[${keyset}]}" "${keyset}" \ + "${expected_keyname}" + expected_keyname="releasekey" + android_choose_key_test_helper "${release_sha[${keyset}]}" "${keyset}" \ + "${expected_keyname}" + done +} + +build_flavor_test_helper() { + local prop_file="${BUILD}/build.prop" + local prop_content="$1" + local expected_flavor_prop="$2" + local flavor_prop="" + + echo "${prop_content}" > "${prop_file}" + flavor_prop=$(android_get_build_flavor_prop "${prop_file}") + if [[ "${flavor_prop}" != "${expected_flavor_prop}" ]]; then + : $(( NUM_TEST_FAILURES += 1 )) + echo "ERROR: Incorrect build flavor '${flavor_prop}' returned." + else + echo "PASS: Correct key name '${flavor_prop}' returned." + fi + rm "${prop_file}" +} + +test_android_get_build_flavor_prop() { + local prop_file="${BUILD}/build.prop" + local prop_content="" + local flavor_prop="" + + echo "TEST: Extract ro.build.flavor property." + prop_content="ro.random.prop=foo +other.prop=bar +x=foobar +ro.build.flavor=cheets_x86-user +another.prop=barfoo" + build_flavor_test_helper "${prop_content}" "cheets_x86-user" + + echo "TEST: Extract single ro.build.flavor property." + prop_content="ro.build.flavor=cheets_x86-user" + build_flavor_test_helper "${prop_content}" "cheets_x86-user" + + echo "TEST: Avoid commented out ro.build.flavor property." + prop_content="ro.random.prop=foo +other.prop=bar +x=foobar +#ro.build.flavor=commented_out +ro.build.flavor=cheets_x86-user +another.prop=barfoo" + build_flavor_test_helper "${prop_content}" "cheets_x86-user" + + # Missing ro.build.flavor property. + echo "TEST: Detect missing ro.build.flavor property." + echo "ro.random.prop=foo" > "${prop_file}" + if flavor_prop=$(android_get_build_flavor_prop "${prop_file}"); then + : $(( NUM_TEST_FAILURES += 1 )) + echo "ERROR: Failed to detect missing ro.build.flavor property." + else + echo "PASS: Detected missing ro.build.flavor property." + fi + rm "${prop_file}" +} + +choose_signing_keyset_test_helper() { + local flavor_prop="$1" + local expected_keyset="$2" + local keyset="" + + keyset=$(android_choose_signing_keyset "${flavor_prop}") + if [[ "${keyset}" != "${expected_keyset}" ]]; then + : $(( NUM_TEST_FAILURES += 1 )) + echo "ERROR: Incorrect keyset '${keyset}' returned instead of \ +'${expected_keyset}'." + else + echo "PASS: Correct keyset '${keyset}' returned." + fi +} + +choose_signing_keyset_test_invalid_flavors() { + local flavor="$1" + + echo "TEST: Detect invalid build flavor '${flavor}'." + if android_choose_signing_keyset "${flavor}"; then + : $(( NUM_TEST_FAILURES += 1 )) + echo "ERROR: Failed to detect invalid build flavor '${flavor}'." + else + echo "PASS: Detected invalid build flavor '${flavor}'." + fi +} + +test_android_choose_signing_keyset() { + echo "TEST: Keyset for aosp_cheets build." + choose_signing_keyset_test_helper "aosp_cheets_x86-userdebug" "aosp" + echo "TEST: Keyset for sdk_google_cheets build." + choose_signing_keyset_test_helper "sdk_google_cheets_x86-userdebug" "cheets" + echo "TEST: Keyset for cheets_x86 build." + choose_signing_keyset_test_helper "cheets_x86-user" "cheets" + echo "TEST: Keyset for cheets_arm build." + choose_signing_keyset_test_helper "cheets_arm-user" "cheets" + echo "TEST: Keyset for cheets_x86_64 build." + choose_signing_keyset_test_helper "cheets_x86_64-user" "cheets" + echo "TEST: Keyset for userdebug build." + choose_signing_keyset_test_helper "cheets_x86-userdebug" "cheets" + + choose_signing_keyset_test_invalid_flavors "aosp" + choose_signing_keyset_test_invalid_flavors "cheets" + choose_signing_keyset_test_invalid_flavors "" + choose_signing_keyset_test_invalid_flavors " " +} + +main() { + if [[ $# -ne 0 ]]; then + echo "FAIL: unexpected arguments '$@'." + return 1 + fi + + BUILD=$(mktemp -d) + echo "Setting temporary build directory as '${BUILD}'." + + test_android_choose_key_invalid_keyset + test_android_choose_key + test_android_get_build_flavor_prop + test_android_choose_signing_keyset + + echo "Deleting temporary build directory '${BUILD}'." + rmdir "${BUILD}" + + if [[ ${NUM_TEST_FAILURES} -gt 0 ]]; then + echo "FAIL: found ${NUM_TEST_FAILURES} failed :(." + return 1 + fi + echo "PASS: all tests passed :)." + return 0 +} + +# Global incremented by each test when they fail. +NUM_TEST_FAILURES=0 +main "$@" |