This repository has been archived by the owner on Dec 19, 2023. It is now read-only.
forked from coreos/scripts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
core_sign_update
executable file
·180 lines (155 loc) · 5.77 KB
/
core_sign_update
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#!/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:<keyname> 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) <actual signature bytes...>
# }
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) <actual signature bytes...>
# }
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."