summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Luebbe <jlu@pengutronix.de>2023-02-13 12:20:37 +0100
committerKhem Raj <raj.khem@gmail.com>2023-02-15 08:23:49 -0800
commit4a6ac691f209204434da38fd271a91455391e728 (patch)
tree70d082cd2700c775c480586b8cf35de767d782bc
parentaaeb992070b1afaba4b4480e0dc817753152c696 (diff)
downloadmeta-openembedded-4a6ac691f209204434da38fd271a91455391e728.tar.gz
add signing.bbclass as infrastructure for build artifact signing
This adds common infrastructure to access and used asymmetric keys to sign build artifacts. The approach and implementation was presented at the recent OpenEmbedded Workshop: https://pretalx.com/openembedded-workshop-2023/talk/3C8MFF/ A working demo setup for verified boot based on qemu is available at https://github.com/jluebbe/meta-code-signing. Signed-off-by: Jan Luebbe <jlu@pengutronix.de> Signed-off-by: Khem Raj <raj.khem@gmail.com>
-rw-r--r--meta-oe/classes/signing.bbclass316
1 files changed, 316 insertions, 0 deletions
diff --git a/meta-oe/classes/signing.bbclass b/meta-oe/classes/signing.bbclass
new file mode 100644
index 0000000000..5c74a319e4
--- /dev/null
+++ b/meta-oe/classes/signing.bbclass
@@ -0,0 +1,316 @@
1#
2# Copyright Jan Luebbe <jlu@pengutronix.de>
3#
4# SPDX-License-Identifier: MIT
5#
6
7# This class provides a common workflow to use asymmetric (i.e. RSA) keys to
8# sign artifacts. Usually, the keys are either stored as simple files in the
9# file system or on a HSM (Hardware Security Module). While files are easy to
10# use, it's hard to verify that no copies of the private have been made and
11# only authorized persons are able to use the key. Use of an HSM addresses
12# these risks by only allowing use of the key via an API (often PKCS #11). The
13# standard way of referring to a specific key in an HSM are PKCS #11 URIs (RFC
14# 7512).
15#
16# Many software projects support signing using PKCS #11 keys, but configuring
17# this is very project specific. Furthermore, as physical HSMs are not very
18# widespread, testing code signing in CI is not simple. To solve this at the
19# build system level, this class takes the approach of always using PKCS #11 at
20# the recipe level. For cases where the keys are available as files (i.e. test
21# keys in CI), they are imported into SoftHSM (a HSM emulation library).
22#
23# Recipes access the available keys via a specific role. So, depending on
24# whether we're building during development or for release, a given role can
25# refer to different keys.
26# Each key recipe PROVIDES a virtual package corresponding to the role, allowing
27# the user to select one of multiple keys for a role when needed.
28#
29# For use with a real HSM, a PKCS #11 URI can be set (i.e. in local.conf) to
30# override the SoftHSM key with the real one:
31#
32# SIGNING_PKCS11_URI[fit] = "pkcs11:serial=DENK0200554;object=ptx-dev-rauc&pin-value=123456"
33# SIGNING_PKCS11_MODULE[fit] = "/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so"
34#
35# Examples for defining roles and importing keys:
36#
37# meta-code-signing/recipes-security/signing-keys/dummy-rsa-key-native.bb
38# meta-code-signing-demo/recipes-security/ptx-dev-keys/ptx-dev-keys-native_git.bb
39#
40# Examples for using keys for signing:
41#
42# meta-code-signing-demo/recipes-security/fit-image/linux-fit-image.bb
43# meta-code-signing-demo/recipes-core/bundles/update-bundle.bb
44#
45# Examples for using keys for authentication:
46#
47# meta-code-signing-demo/recipes-security/fit-image/barebox_%.bbappend
48# meta-code-signing-demo/recipes-core/rauc/rauc_%.bbappend
49#
50# Examples for using keys for both signing and authentication:
51#
52# meta-code-signing-demo/recipes-kernel/linux/linux-yocto_6.1.bbappend
53
54SIGNING_PKCS11_URI ?= ""
55SIGNING_PKCS11_MODULE ?= ""
56
57DEPENDS += "softhsm-native libp11-native opensc-native openssl-native"
58
59def signing_class_prepare(d):
60 import os.path
61
62 def export(role, k, v):
63 k = k % (role, )
64 d.setVar(k, v)
65 d.setVarFlag(k, "export", "1")
66
67 roles = set()
68 roles |= (d.getVarFlags("SIGNING_PKCS11_URI") or {}).keys()
69 roles |= (d.getVarFlags("SIGNING_PKCS11_MODULE") or {}).keys()
70 for role in roles:
71 if not set(role).issubset("abcdefghijklmnopqrstuvwxyz0123456789_"):
72 bb.fatal("key role name '%s' must consist of only [a-z0-9_]" % (role,))
73
74 pkcs11_uri = d.getVarFlag("SIGNING_PKCS11_URI", role) or d.getVar("SIGNING_PKCS11_URI")
75 if not pkcs11_uri.startswith("pkcs11:"):
76 bb.fatal("URI for key role '%s' must start with 'pkcs11:'" % (role,))
77
78 pkcs11_module = d.getVarFlag("SIGNING_PKCS11_MODULE", role) or d.getVar("SIGNING_PKCS11_MODULE")
79 if not os.path.isfile(pkcs11_module):
80 bb.fatal("module path for key role '%s' must be an existing file" % (role,))
81
82 if pkcs11_uri and not pkcs11_module:
83 bb.warn("SIGNING_PKCS11_URI[%s] is set without SIGNING_PKCS11_MODULE[%s]" % (role, role))
84 if pkcs11_module and not pkcs11_uri:
85 bb.warn("SIGNING_PKCS11_MODULE[%s] is set without SIGNING_PKCS11_URI[%s]" % (role, role))
86
87 export(role, "SIGNING_PKCS11_URI_%s_", pkcs11_uri)
88 export(role, "SIGNING_PKCS11_MODULE_%s_", pkcs11_module)
89
90signing_pkcs11_tool() {
91 pkcs11-tool --module "${STAGING_LIBDIR_NATIVE}/softhsm/libsofthsm2.so" --login --pin 1111 $*
92}
93
94signing_import_prepare() {
95 export _SIGNING_ENV_FILE_="${B}/meta-signing.env"
96 rm -f "$_SIGNING_ENV_FILE_"
97
98 export SOFTHSM2_CONF="${B}/softhsm2.conf"
99 export SOFTHSM2_DIR="${B}/softhsm2.tokens"
100 export SOFTHSM2_MOD="${STAGING_LIBDIR_NATIVE}/softhsm/libsofthsm2.so"
101
102 echo "directories.tokendir = $SOFTHSM2_DIR" > "$SOFTHSM2_CONF"
103 echo "objectstore.backend = db" >> "$SOFTHSM2_CONF"
104 rm -rf "$SOFTHSM2_DIR"
105 mkdir -p "$SOFTHSM2_DIR"
106
107 softhsm2-util --module $SOFTHSM2_MOD --init-token --free --label ${PN} --pin 1111 --so-pin 222222
108}
109
110signing_import_define_role() {
111 local role="${1}"
112 case "${1}" in
113 (*[!a-z0-9_]*) false;;
114 (*) true;;
115 esac || bbfatal "invalid role name '${1}', must consist of [a-z0-9_]"
116
117 echo "_SIGNING_PKCS11_URI_${role}_=\"pkcs11:token=${PN};object=$role;pin-value=1111\"" >> $_SIGNING_ENV_FILE_
118 echo "_SIGNING_PKCS11_MODULE_${role}_=\"softhsm\"" >> $_SIGNING_ENV_FILE_
119}
120
121# signing_import_cert_from_der <role> <der>
122#
123# Import a certificate from DER file to a role. To be used
124# with SoftHSM.
125signing_import_cert_from_der() {
126 local role="${1}"
127 local der="${2}"
128
129 signing_pkcs11_tool --type cert --write-object "${der}" --label "${role}"
130}
131
132# signing_import_cert_from_pem <role> <pem>
133#
134# Import a certificate from PEM file to a role. To be used
135# with SoftHSM.
136signing_import_cert_from_pem() {
137 local role="${1}"
138 local pem="${2}"
139
140 openssl x509 \
141 -in "${pem}" -inform pem -outform der |
142 signing_pkcs11_tool --type cert --write-object /proc/self/fd/0 --label "${role}"
143}
144
145# signing_import_pubkey_from_der <role> <pem>
146#
147# Import a public key from DER file to a role. To be used with SoftHSM.
148signing_import_pubkey_from_pem() {
149 local role="${1}"
150 local der="${2}"
151
152 signing_pkcs11_tool --type pubkey --write-object "${der}" --label "${role}"
153}
154
155# signing_import_pubkey_from_pem <role> <pem>
156#
157# Import a public key from PEM file to a role. To be used with SoftHSM.
158signing_import_pubkey_from_pem() {
159 local openssl_keyopt
160 local role="${1}"
161 local pem="${2}"
162
163 if [ -n "${IMPORT_PASS_FILE}" ]; then
164 openssl rsa \
165 -passin "file:${IMPORT_PASS_FILE}" \
166 -in "${pem}" -inform pem -pubout -outform der
167 else
168 openssl rsa \
169 -in "${pem}" -inform pem -pubout -outform der
170 fi |
171 signing_pkcs11_tool --type pubkey --write-object /proc/self/fd/0 --label "${role}"
172}
173
174# signing_import_privkey_from_der <role> <pem>
175#
176# Import a private key from DER file to a role. To be used with SoftHSM.
177signing_import_privkey_from_der() {
178 local role="${1}"
179 local der="${2}"
180 signing_pkcs11_tool --type privkey --write-object "${der}" --label "${role}"
181}
182
183# signing_import_privkey_from_pem <role> <pem>
184#
185# Import a private key from PEM file to a role. To be used with SoftHSM.
186signing_import_privkey_from_pem() {
187 local openssl_keyopt
188 local role="${1}"
189 local pem="${2}"
190
191 if [ -n "${IMPORT_PASS_FILE}" ]; then
192 openssl rsa \
193 -passin "file:${IMPORT_PASS_FILE}" \
194 -in "${pem}" -inform pem -outform der
195 else
196 openssl rsa \
197 -in "${pem}" -inform pem -outform der
198 fi |
199 signing_pkcs11_tool --type privkey --write-object /proc/self/fd/0 --label "${role}"
200}
201
202# signing_import_key_from_pem <role> <pem>
203#
204# Import a private and public key from PEM file to a role. To be used
205# with SoftHSM.
206signing_import_key_from_pem() {
207 local role="${1}"
208 local pem="${2}"
209
210 signing_import_pubkey_from_pem "${role}" "${pem}"
211 signing_import_privkey_from_pem "${role}" "${pem}"
212}
213
214signing_import_finish() {
215 echo "loaded objects:"
216 signing_pkcs11_tool --list-objects
217}
218
219signing_import_install() {
220 install -d ${D}${localstatedir}/lib/softhsm/tokens/${PN}
221 install -m 600 -t ${D}${localstatedir}/lib/softhsm/tokens/${PN} ${B}/softhsm2.tokens/*/*
222 install -d ${D}${localstatedir}/lib/meta-signing.env.d
223 install -m 644 "${B}/meta-signing.env" ${D}${localstatedir}/lib/meta-signing.env.d/${PN}
224}
225
226signing_prepare() {
227 if [ -f ${OPENSSL_CONF} ]; then
228 echo "Using '${OPENSSL_CONF}' for OpenSSL configuration"
229 else
230 echo "Missing 'openssl.cnf' at '${STAGING_ETCDIR_NATIVE}/ssl'"
231 return 1
232 fi
233 if [ -d ${OPENSSL_MODULES} ]; then
234 echo "Using '${OPENSSL_MODULES}' for OpenSSL run-time modules"
235 else
236 echo "Missing OpenSSL module directory at '${OPENSSL_MODULES}'"
237 return 1
238 fi
239 if [ -d ${OPENSSL_ENGINES} ]; then
240 echo "Using '${OPENSSL_ENGINES}' for OpenSSL run-time PKCS#11 modules"
241 else
242 echo "Missing OpenSSL PKCS11 engine directory at '${OPENSSL_ENGINES}'"
243 return 1
244 fi
245
246 export SOFTHSM2_CONF="${WORKDIR}/softhsm2.conf"
247 export SOFTHSM2_DIR="${STAGING_DIR_NATIVE}/var/lib/softhsm/tokens"
248
249 echo "directories.tokendir = $SOFTHSM2_DIR" > "$SOFTHSM2_CONF"
250 echo "objectstore.backend = db" >> "$SOFTHSM2_CONF"
251
252 for env in $(ls "${STAGING_DIR_NATIVE}/var/lib/meta-signing.env.d"); do
253 . "${STAGING_DIR_NATIVE}/var/lib/meta-signing.env.d/$env"
254 done
255}
256# make sure these functions are exported
257signing_prepare[vardeps] += "signing_get_uri signing_get_module"
258
259signing_use_role() {
260 local role="${1}"
261
262 export PKCS11_MODULE_PATH="$(signing_get_module $role)"
263 export PKCS11_URI="$(signing_get_uri $role)"
264
265 if [ -z "$PKCS11_MODULE_PATH" ]; then
266 echo "No PKCS11_MODULE_PATH found for role '${role}'"
267 exit 1
268 fi
269 if [ -z "$PKCS11_URI" ]; then
270 echo "No PKCS11_URI found for role '${role}'"
271 exit 1
272 fi
273}
274
275signing_get_uri() {
276 local role="${1}"
277
278 # prefer local configuration
279 eval local uri="\$SIGNING_PKCS11_URI_${role}_"
280 if [ -n "$uri" ]; then
281 echo "$uri"
282 return
283 fi
284
285 # fall back to softhsm
286 eval echo "\$_SIGNING_PKCS11_URI_${role}_"
287}
288
289signing_get_module() {
290 local role="${1}"
291
292 # prefer local configuration
293 eval local module="\$SIGNING_PKCS11_MODULE_${role}_"
294 if [ -n "$module" ]; then
295 echo "$module"
296 return
297 fi
298
299 # fall back to softhsm
300 eval local module="\$_SIGNING_PKCS11_MODULE_${role}_"
301 if [ "$module" = "softhsm" ]; then
302 echo "${STAGING_LIBDIR_NATIVE}/softhsm/libsofthsm2.so"
303 else
304 echo "$module"
305 fi
306}
307
308python () {
309 signing_class_prepare(d)
310}
311
312export OPENSSL_MODULES="${STAGING_LIBDIR_NATIVE}/ossl-modules"
313export OPENSSL_ENGINES="${STAGING_LIBDIR_NATIVE}/engines-3"
314export OPENSSL_CONF="${STAGING_LIBDIR_NATIVE}/ssl-3/openssl.cnf"
315export SSL_CERT_DIR="${STAGING_LIBDIR_NATIVE}/ssl-3/certs"
316export SSL_CERT_FILE="${STAGING_LIBDIR_NATIVE}/ssl-3/cert.pem"