Skip to content
This repository was archived by the owner on Aug 7, 2025. It is now read-only.

Commit e02cab5

Browse files
William Douglasbryteise
authored andcommitted
Add support for an alternative swupd certificate
Enable an alternative swupd certificate location (the location of the default or given cert with an ".alt" appended to it). The purpose of this change is to allow more reliable and flexible key rotations. If either the main cert or alt cert fails when doing content verification then the other will be tried (and be used for the next operation). In this way, as long as both certs don't fail for the same content verification, progress can be made with either cert. Signed-off-by: William Douglas <william.douglas@intel.com>
1 parent 0d1591f commit e02cab5

File tree

2 files changed

+164
-2
lines changed

2 files changed

+164
-2
lines changed

src/swupd_lib/signature.c

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ static X509 *get_cert_from_path(const char *certificate_path);
4949

5050
static X509_STORE *store = NULL;
5151
static STACK_OF(X509) *x509_stack = NULL;
52+
static char *orig_cert = NULL;
53+
static char *alt_cert = NULL;
5254

5355
static int verify_callback_ignore_expiration(int ok, X509_STORE_CTX *local_store)
5456
{
@@ -174,8 +176,7 @@ void signature_deinit(void)
174176
CRYPTO_cleanup_all_ex_data();
175177
}
176178

177-
bool signature_verify_data(const void *data, size_t data_len, const void *sig_data, size_t sig_data_len, enum signature_flags flags)
178-
179+
static bool signature_verify_data_internal(const void *data, size_t data_len, const void *sig_data, size_t sig_data_len, enum signature_flags flags)
179180
{
180181
bool result = false;
181182
int ret;
@@ -267,6 +268,64 @@ bool signature_verify_data(const void *data, size_t data_len, const void *sig_da
267268
return result;
268269
}
269270

271+
static bool swap_certs(void)
272+
{
273+
bool ret = true;
274+
275+
signature_deinit();
276+
if (str_cmp(globals.cert_path, orig_cert) == 0) {
277+
set_cert_path(alt_cert);
278+
} else {
279+
set_cert_path(orig_cert);
280+
}
281+
if (!signature_init(globals.cert_path, NULL)) {
282+
ret = false;
283+
}
284+
285+
return ret;
286+
}
287+
288+
static bool use_alt_cert(void)
289+
{
290+
bool ret = true;
291+
292+
if (!globals.cert_path) {
293+
return false;
294+
}
295+
296+
/* This function can be called multiple times and
297+
* is intended to be able to handle swapping back
298+
* and forth between main and alt cert files. */
299+
if (!orig_cert && !alt_cert) {
300+
orig_cert = strdup_or_die(globals.cert_path);
301+
string_or_die(&alt_cert, "%s.alt", globals.cert_path);
302+
if (sys_file_exists(alt_cert)) {
303+
warn("Default cert failed, attempting to use alternative: %s\n", alt_cert);
304+
ret = swap_certs();
305+
} else {
306+
FREE(alt_cert);
307+
alt_cert = NULL;
308+
ret = false;
309+
}
310+
} else if (!alt_cert) {
311+
ret = false;
312+
} else {
313+
ret = swap_certs();
314+
}
315+
316+
return ret;
317+
}
318+
319+
bool signature_verify_data(const void *data, size_t data_len, const void *sig_data, size_t sig_data_len, enum signature_flags flags)
320+
{
321+
bool result = signature_verify_data_internal(data, data_len, sig_data, sig_data_len, flags);
322+
if (!result && use_alt_cert()) {
323+
result = signature_verify_data_internal(data, data_len, sig_data, sig_data_len, flags);
324+
}
325+
326+
return result;
327+
}
328+
270329
bool signature_verify(const char *file, const char *sig_file, enum signature_flags flags)
271330
{
272331
bool result = false;
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/usr/bin/env bats
2+
3+
# Author: William Douglas
4+
# Email: william.douglas@intel.com
5+
6+
load "../testlib"
7+
8+
test_setup() {
9+
10+
create_test_environment -r "$TEST_NAME" 10 1
11+
export CERT_PATH="/usr/share/clear/update-ca/Swupd_Root.pem"
12+
export ALT_CERT_PATH="$CERT_PATH.alt"
13+
export SWUPD_OPTS_EXTRA="$SWUPD_OPTS_NO_FMT_NO_CERT -C $TARGET_DIR$CERT_PATH"
14+
15+
create_version -r "$TEST_NAME" 20 10
16+
generate_certificate "$TEST_NAME/new_root.key" "$TEST_NAME/new_root.pem"
17+
update_bundle -p "$TEST_NAME" os-core --add "$CERT_PATH":"$ABS_TEST_DIR"/Swupd_Root.pem
18+
update_bundle "$TEST_NAME" os-core --add "$ALT_CERT_PATH":"$TEST_NAME/new_root.pem"
19+
bump_format "$TEST_NAME"
20+
21+
create_version -r "$TEST_NAME" 50 40 2
22+
23+
}
24+
25+
@test "SIG029: alt key usable" {
26+
27+
# Test the alternate keyfile is usable
28+
29+
# file only used in check-update so not needed here
30+
# sudo openssl smime -sign -binary -in "$WEB_DIR"/version/latest_version -signer "$TEST_NAME/new_root.pem" -inkey "$TEST_NAME/new_root.key" -out "$WEB_DIR"/version/latest_version.sig -outform DER
31+
32+
# use the new cert to force the alt cert into use for the first time
33+
sudo openssl smime -sign -binary -in "$WEB_DIR"/version/format2/latest -signer "$TEST_NAME/new_root.pem" -inkey "$TEST_NAME/new_root.key" -out "$WEB_DIR"/version/format2/latest.sig -outform DER
34+
35+
# Sign the MoM with self signed intermediate cert
36+
# This is verified first, use old cert to test swapping from alt to original certs
37+
# sudo openssl smime -sign -binary -in "$WEB_DIR"/40/Manifest.MoM -signer "$TEST_NAME/new_root.pem" -inkey "$TEST_NAME/new_root.key" -out "$WEB_DIR"/40/Manifest.MoM.sig -outform DER
38+
39+
# verified second, use new cert to test two cert swaps are usable in the same update
40+
# and different manifest versions can be verified with different certs
41+
sudo openssl smime -sign -binary -in "$WEB_DIR"/50/Manifest.MoM -signer "$TEST_NAME/new_root.pem" -inkey "$TEST_NAME/new_root.key" -out "$WEB_DIR"/50/Manifest.MoM.sig -outform DER
42+
43+
run sudo sh -c "$SWUPD update -V 20 $SWUPD_OPTS_NO_FMT"
44+
assert_status_is "$SWUPD_OK"
45+
46+
assert_file_exists "$TARGET_DIR""$CERT_PATH"
47+
assert_file_exists "$TARGET_DIR""$ALT_CERT_PATH"
48+
run sudo sh -c "$SWUPD update -V 30 $SWUPD_OPTS_EXTRA"
49+
50+
assert_status_is "$SWUPD_OK"
51+
expected_output=$(cat <<-EOM
52+
Update started
53+
Preparing to update from 20 to 30
54+
Downloading packs for:
55+
- os-core
56+
Finishing packs extraction...
57+
Statistics for going from version 20 to version 30:
58+
changed bundles : 1
59+
new bundles : 0
60+
deleted bundles : 0
61+
changed files : 2
62+
new files : 0
63+
deleted files : 0
64+
Validate downloaded files
65+
No extra files need to be downloaded
66+
Installing files...
67+
Update was applied
68+
Calling post-update helper scripts
69+
Update successful - System updated from version 20 to version 30
70+
EOM
71+
)
72+
assert_is_output "$expected_output"
73+
assert_file_exists "$TARGET_DIR"/core
74+
75+
run sudo sh -c "$SWUPD update -V 50 $SWUPD_OPTS_EXTRA"
76+
77+
assert_status_is "$SWUPD_OK"
78+
expected_output=$(cat <<-EOM
79+
Update started
80+
Warning: Default cert failed, attempting to use alternative: .*alt
81+
Preparing to update from 40 to 50
82+
Downloading packs for:
83+
- os-core
84+
Finishing packs extraction...
85+
Statistics for going from version 40 to version 50:
86+
changed bundles : 1
87+
new bundles : 0
88+
deleted bundles : 0
89+
changed files : 2
90+
new files : 0
91+
deleted files : 0
92+
Validate downloaded files
93+
No extra files need to be downloaded
94+
Installing files...
95+
Update was applied
96+
Calling post-update helper scripts
97+
Update successful - System updated from version 40 to version 50
98+
EOM
99+
)
100+
assert_regex_is_output "$expected_output"
101+
assert_file_exists "$TARGET_DIR"/core
102+
103+
}

0 commit comments

Comments
 (0)