Skip to content

Commit

Permalink
Add support for an alternative swupd certificate
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
bryteise committed Aug 29, 2024
1 parent 0d1591f commit fa77119
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 2 deletions.
63 changes: 61 additions & 2 deletions src/swupd_lib/signature.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ static X509 *get_cert_from_path(const char *certificate_path);

static X509_STORE *store = NULL;
static STACK_OF(X509) *x509_stack = NULL;
static char *orig_cert = NULL;
static char *alt_cert = NULL;

static int verify_callback_ignore_expiration(int ok, X509_STORE_CTX *local_store)
{
Expand Down Expand Up @@ -174,8 +176,7 @@ void signature_deinit(void)
CRYPTO_cleanup_all_ex_data();
}

bool signature_verify_data(const void *data, size_t data_len, const void *sig_data, size_t sig_data_len, enum signature_flags flags)

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)
{
bool result = false;
int ret;
Expand Down Expand Up @@ -267,6 +268,64 @@ bool signature_verify_data(const void *data, size_t data_len, const void *sig_da
return result;
}

static bool swap_certs(void)
{
bool ret = true;

signature_deinit();
if (str_cmp(globals.cert_path, orig_cert) == 0) {
set_cert_path(alt_cert);
} else {
set_cert_path(orig_cert);
}
if (!signature_init(globals.cert_path, NULL)) {
ret = false;
}

return ret;
}

static bool use_alt_cert(void)
{
bool ret = true;

if (!globals.cert_path) {
return false;
}

/* This function can be called multiple times and
* is intended to be able to handle swapping back
* and forth between main and alt cert files. */
if (!orig_cert && !alt_cert) {
orig_cert = strdup_or_die(globals.cert_path);
string_or_die(&alt_cert, "%s.alt", globals.cert_path);
if (sys_file_exists(alt_cert)) {
warn("Default cert failed, attempting to use alternative: %s\n", alt_cert);
ret = swap_certs();
} else {
FREE(alt_cert);
alt_cert = NULL;
ret = false;
}
} else if (!alt_cert) {
ret = false;
} else {
ret = swap_certs();
}

return ret;
}

bool signature_verify_data(const void *data, size_t data_len, const void *sig_data, size_t sig_data_len, enum signature_flags flags)
{
bool result = signature_verify_data_internal(data, data_len, sig_data, sig_data_len, flags);
if (!result && use_alt_cert()) {
result = signature_verify_data_internal(data, data_len, sig_data, sig_data_len, flags);
}

return result;
}

bool signature_verify(const char *file, const char *sig_file, enum signature_flags flags)
{
bool result = false;
Expand Down
103 changes: 103 additions & 0 deletions test/functional/signature/alt-key-rotation.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/usr/bin/env bats

# Author: William Douglas
# Email: [email protected]

load "../testlib"

test_setup() {

create_test_environment -r "$TEST_NAME" 10 1
export CERT_PATH="/usr/share/clear/update-ca/Swupd_Root.pem"
export ALT_CERT_PATH="$CERT_PATH.alt"
export SWUPD_OPTS_EXTRA="$SWUPD_OPTS_NO_FMT_NO_CERT -C $TARGET_DIR$CERT_PATH"

create_version -r "$TEST_NAME" 20 10
generate_certificate "$TEST_NAME/new_root.key" "$TEST_NAME/new_root.pem"
update_bundle -p "$TEST_NAME" os-core --add "$CERT_PATH":"$ABS_TEST_DIR"/Swupd_Root.pem
update_bundle "$TEST_NAME" os-core --add "$ALT_CERT_PATH":"$TEST_NAME/new_root.pem"
bump_format "$TEST_NAME"

create_version -r "$TEST_NAME" 50 40 2

}

@test "SIG029: alt key usable" {

# Test the alternate keyfile is usable

# file only used in check-update so not needed here
# 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

# use the new cert to force the alt cert into use for the first time
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

# Sign the MoM with self signed intermediate cert
# This is verified first, use old cert to test swapping from alt to original certs
# 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

# verified second, use new cert to test two cert swaps are usable in the same update
# and different manifest versions can be verified with different certs
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

run sudo sh -c "$SWUPD update -V 20 $SWUPD_OPTS_NO_FMT"
assert_status_is "$SWUPD_OK"

assert_file_exists "$TARGET_DIR""$CERT_PATH"
assert_file_exists "$TARGET_DIR""$ALT_CERT_PATH"
run sudo sh -c "$SWUPD update -V 30 $SWUPD_OPTS_EXTRA"

assert_status_is "$SWUPD_OK"
expected_output=$(cat <<-EOM
Update started
Preparing to update from 20 to 30
Downloading packs for:
- os-core
Finishing packs extraction...
Statistics for going from version 20 to version 30:
changed bundles : 1
new bundles : 0
deleted bundles : 0
changed files : 2
new files : 0
deleted files : 0
Validate downloaded files
No extra files need to be downloaded
Installing files...
Update was applied
Calling post-update helper scripts
Update successful - System updated from version 20 to version 30
EOM
)
assert_is_output "$expected_output"
assert_file_exists "$TARGET_DIR"/core

run sudo sh -c "$SWUPD update -V 50 $SWUPD_OPTS_EXTRA"

assert_status_is "$SWUPD_OK"
expected_output=$(cat <<-EOM
Update started
Warning: Default cert failed, attempting to use alternative: .*alt
Preparing to update from 40 to 50
Downloading packs for:
- os-core
Finishing packs extraction...
Statistics for going from version 40 to version 50:
changed bundles : 1
new bundles : 0
deleted bundles : 0
changed files : 2
new files : 0
deleted files : 0
Validate downloaded files
No extra files need to be downloaded
Installing files...
Update was applied
Calling post-update helper scripts
Update successful - System updated from version 40 to version 50
EOM
)
assert_regex_is_output "$expected_output"
assert_file_exists "$TARGET_DIR"/core

}

0 comments on commit fa77119

Please sign in to comment.