#!/usr/bin/env bash # Copyright (c) 2014 CoreOS, Inc. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # usage: ./core_generate_update_payload --image coreos_production_update.bin \ # --output update.gz \ # --private_keys update.key.pem:update2.key.pem # --public_keys update.pub.pem:update2.pub.pem SCRIPT_ROOT=$(dirname $(readlink -f "$0")) # We have to simple-mindedly set GCLIENT_ROOT in case we're running from # au-generator.zip because common.sh will fail while auto-detect it. export GCLIENT_ROOT=$(readlink -f "${SCRIPT_ROOT}/../../") . "${SCRIPT_ROOT}/common.sh" || exit 1 DEFINE_string image "" "The filesystem image of /usr" DEFINE_string kernel "" "The kernel image" DEFINE_string output "" "Output file" DEFINE_string private_keys "" "Path, pkcs11 URI, or fero: for private keys." DEFINE_string public_keys "" "Path to public keys in .pem format." DEFINE_string keys_separator ":" "Separator for the above keys" DEFINE_string user_signatures "" \ "Colon-separated paths to user signatures to provide to signing server" DEFINE_string signing_server_address "" "Hostname of the signing server" DEFINE_integer signing_server_port "50051" "Port of the signing server" # Parse command line FLAGS "$@" || exit 1 eval set -- "${FLAGS_ARGV}" set -e cleanup() { rm -f padding rm -f padding-pkcs11 rm -f update rm -f update.hash rm -f update.padhash rm -f update.pkcs11-padhash rm -f update.signed rm -f update.sig.* } trap cleanup INT TERM EXIT echo "=== Creating signable update payload... ===" delta_generator \ -new_image "$FLAGS_image" \ -new_kernel "$FLAGS_kernel" \ -out_file update # The separator is configurable for backwards compatibility with old `sign.sh` scripts. IFS="${FLAGS_keys_separator}" read -a private_keys <<< "$FLAGS_private_keys" IFS="${FLAGS_keys_separator}" read -a public_keys <<< "$FLAGS_public_keys" if [ ${#private_keys[@]} -ne ${#public_keys[@]} ]; then echo "mismatch in count of private keys and public keys" exit 1 fi i=0 signature_sizes="" for key in "${private_keys[@]}"; do signature_sizes=${signature_sizes}:256 let "i += 1" done signature_sizes="${signature_sizes:1:${#signature_sizes}}" # We don't need to maintain backwards compatibility with old `sign.sh` scripts here, so we only # allow colon-separated values for user signature files. IFS=":" read -a user_signatures <<< "$FLAGS_user_signatures" user_signatures_arg="" for user_signature in "${user_signatures[@]}"; do user_signatures_arg="${user_signatures_arg} --signature ${user_signature}" done user_signatures_arg="${user_signatures_arg:1:${#user_signatures_arg}}" delta_generator \ --signature_size ${signature_sizes} \ --in_file update \ --out_hash_file update.hash # padding for openssl rsautl -pkcs (smartcard keys) # # The following is an ASN.1 header. It is prepended to the actual signature # (32 bytes) to form a sequence of 51 bytes. OpenSSL will add additional # PKCS#1 1.5 padding during the signing operation. The padded hash will look # as follows: # # ASN1HEADER SHA256HASH # |----19----||----32----| # # where ASN1HEADER is the ASN.1 description of the signed data. The complete 51 # bytes of actual data (i.e. the ASN.1 header complete with the hash) are # packed as follows: # # SEQUENCE(2+49) { # SEQUENCE(2+13) { # OBJECT(2+9) id-sha256 # NULL(2+0) # } # OCTET STRING(2+32) # } echo "MDEwDQYJYIZIAWUDBAIBBQAEIA==" | base64 -d > padding-pkcs11 cat padding-pkcs11 update.hash > update.pkcs11-padhash # Legacy padding for openssl -raw (non smartcard keys) # # The following is a standard PKCS1-v1_5 padding for SHA256 signatures, as # defined in RFC3447. It is prepended to the actual signature (32 bytes) to # form a sequence of 256 bytes (2048 bits) that is amenable to RSA signing. The # padded hash will look as follows: # # 0x00 0x01 0xff ... 0xff 0x00 ASN1HEADER SHA256HASH # |--------------205-----------||----19----||----32----| # # where ASN1HEADER is the ASN.1 description of the signed data. The complete 51 # bytes of actual data (i.e. the ASN.1 header complete with the hash) are # packed as follows: # # SEQUENCE(2+49) { # SEQUENCE(2+13) { # OBJECT(2+9) id-sha256 # NULL(2+0) # } # OCTET STRING(2+32) # } echo "AAH/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////ADAxMA0GCWCGSAFlAwQCAQUABCA=" | base64 -d > padding cat padding update.hash > update.padhash echo "=== Signing update payload... ===" i=1 signature_sizes="" for key in "${private_keys[@]}"; do if [[ "${key}" == pkcs11* ]]; then openssl rsautl -engine pkcs11 -pkcs -sign -inkey ${key} -keyform engine -in update.pkcs11-padhash -out update.sig.${i} elif [[ "${key}" == fero* ]]; then fero-client \ --address $FLAGS_signing_server_address \ --port $FLAGS_signing_server_port \ sign --pkcs1 \ --file update.hash \ --output update.sig.${i} \ --secret-key ${key:5:${#key}} \ ${user_signatures_arg} else openssl rsautl -raw -sign -inkey ${key} -in update.padhash -out update.sig.${i} fi let "i += 1" done files="" for i in update.sig.*; do files=${files}:${i} done files="${files:1:${#files}}" echo ${files} delta_generator --signature_file ${files} --in_file update --out_file update.signed i=1 for key in "${public_keys[@]}"; do delta_generator \ --public_key_version "${i}" \ --public_key "${key}" \ --in_file update.signed let "i += 1" done mv update.signed ${FLAGS_output} echo "=== Update payload signed successfully. ===" trap - INT TERM EXIT cleanup noexit echo "Done generating full update."