1
0
mirror of https://github.com/ostreedev/ostree.git synced 2026-02-05 09:44:55 +01:00
Files
ostree/tests/test-signed-commit-spki.sh
Daiki Ueno 79470298bf sign: Support generic "spki" type of commit signatures
The current "ed25519" signing type assumes raw Ed25519 key format for
both public and private keys. This patch generalizes it by adding a
new signature type "spki" which uses the X.509 SubjectPublicKeyInfo
format for public keys. Keys in this format can easily be created with
openssl tools and provide crypto agility[1] as the format embeds
algorithm identifier.

The supposed use-case of this feature is to attach multiple signatures
with different algorithms to a single commit, so even if an algorithm
turned vulnerable, the signatures made with other algorithms can still
be used as a fallback. For instance, signer can create an Ed25519
signature along with a quantum-resistent ML-DSA signature.

The following are a couple of implementation notes:

- The private keys shall be stored in the PKCS#8 format, though future
  extensions may support other format such as opaque key handles on a
  hardware token.

- The "spki" signature type prefers the keys to be encoded in the PEM
  format on disk, while it still accepts base64 encoded keys when given
  through the command-line.

1. https://en.wikipedia.org/wiki/Cryptographic_agility

Signed-off-by: Daiki Ueno <dueno@redhat.com>
2025-03-18 16:22:16 +09:00

165 lines
7.0 KiB
Bash
Executable File

#!/bin/bash
#
# Copyright (C) 2019 Collabora Ltd.
#
# SPDX-License-Identifier: LGPL-2.0+
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <https://www.gnu.org/licenses/>.
set -euo pipefail
. $(dirname $0)/libtest.sh
# This is explicitly opt in for testing
export OSTREE_DUMMY_SIGN_ENABLED=1
mkdir ${test_tmpdir}/repo
ostree_repo_init repo --mode="archive"
# For multi-sign test
DUMMYSIGN="dummysign"
# Test ostree sign with 'spki' module
gen_spki_keys
PUBLIC=${SPKIPUBLIC}
SECRET=${SPKISECRET}
WRONG_PUBLIC="$(gen_spki_random_public)"
echo "PUBLIC = $PUBLIC"
echo "Signed commit with spki: ${SECRET}" >> file.txt
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo commit -b main -s "Signed with spki module" --sign="${SECRET}" --sign-type=spki
COMMIT="$(ostree --repo=${test_tmpdir}/repo rev-parse main)"
# Ensure that detached metadata contain signature
${CMD_PREFIX} ostree --repo=repo show ${COMMIT} --print-detached-metadata-key=ostree.sign.spki &>/dev/null
tap_ok "Detached spki signature added"
# Verify vith sign mechanism
if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki ${COMMIT} ${WRONG_PUBLIC}; then
exit 1
fi
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki ${COMMIT} ${PUBLIC}
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki ${COMMIT} ${PUBLIC} ${PUBLIC}
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki ${COMMIT} $(gen_spki_random_public) ${PUBLIC}
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki ${COMMIT} $(gen_spki_random_public) $(gen_spki_random_public) ${PUBLIC}
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki ${COMMIT} ${PUBLIC} $(gen_spki_random_public) $(gen_spki_random_public)
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki ${COMMIT} $(gen_spki_random_public) $(gen_spki_random_public) ${PUBLIC} $(gen_spki_random_public) $(gen_spki_random_public)
tap_ok "spki signature verified"
# Check if we are able to use all available modules to sign the same commit
echo "Unsigned commit for multi-sign" >> file.txt
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo commit -b main -s 'Unsigned commit'
COMMIT="$(ostree --repo=${test_tmpdir}/repo rev-parse main)"
# Check if we have no signatures
for mod in "dummy" "spki"; do
if ostree --repo=repo show ${COMMIT} --print-detached-metadata-key=ostree.sign.${mod}; then
echo "Unexpected signature for ${mod} found"
exit 1
fi
done
# Sign with all available modules
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy ${COMMIT} ${DUMMYSIGN}
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=spki ${COMMIT} ${SECRET}
# and verify
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki ${COMMIT} ${PUBLIC} >out.txt
assert_file_has_content out.txt "spki: Signature verified successfully with key"
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy --verify ${COMMIT} ${DUMMYSIGN} >out.txt
assert_file_has_content out.txt "dummy: Signature verified"
tap_ok "multiple signing "
# Prepare files with public spki signatures
PUBKEYS="$(mktemp -p ${test_tmpdir} spki_XXXXXX.spki)"
# Test if file contain no keys
if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-file=${PUBKEYS} ${COMMIT}; then
exit 1
fi
# Test if have a problem with file object
if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-file=${test_tmpdir} ${COMMIT}; then
exit 1
fi
# Test with single key in list
cat ${SPKIPUBLICPEM} > ${PUBKEYS}
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-file=${PUBKEYS} ${COMMIT} >out.txt
assert_file_has_content out.txt 'spki: Signature verified successfully'
# Test the file with multiple keys without a valid public key
for((i=0;i<100;i++)); do
# Generate a list with some public signatures
gen_spki_random_public_pem
done > ${PUBKEYS}
# Check if file contain no valid signatures
if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-file=${PUBKEYS} ${COMMIT} 2>err.txt; then
fatal "validated with no signatures"
fi
assert_file_has_content err.txt 'error:.* spki: Signature couldn.t be verified; tried 100 keys'
# Check if no valid signatures provided via args&file
if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-file=${PUBKEYS} ${COMMIT} ${WRONG_PUBLIC}; then
exit 1
fi
#Test keys file and public key
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-file=${PUBKEYS} ${COMMIT} ${PUBLIC}
# Add correct key into the list
cat "${SPKIPUBLICPEM}" >> ${PUBKEYS}
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-file=${PUBKEYS} ${COMMIT}
tap_ok "verify spki keys file"
# Check spki signing with secret file
echo "Unsigned commit for secret file usage" >> file.txt
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo commit -b main -s 'Unsigned commit'
COMMIT="$(ostree --repo=${test_tmpdir}/repo rev-parse main)"
KEYFILE="$(mktemp -p ${test_tmpdir} secret_XXXXXX.spki)"
cat "${SPKISECRETPEM}" > ${KEYFILE}
# Sign
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=spki --keys-file=${KEYFILE} ${COMMIT}
# Verify
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-file=${PUBKEYS} ${COMMIT}
tap_ok "sign with spki keys file"
# Check the well-known places mechanism
mkdir -p ${test_tmpdir}/{trusted,revoked}.spki.d
for((i=0;i<100;i++)); do
# Generate some key files with random public signatures
gen_spki_random_public_pem > ${test_tmpdir}/trusted.spki.d/signature_$i
done
# Check no valid public keys are available
if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-dir=${test_tmpdir} ${COMMIT}; then
exit 1
fi
cat "${SPKIPUBLICPEM}" > ${test_tmpdir}/trusted.spki.d/correct
# Verify with correct key
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-dir=${test_tmpdir} ${COMMIT}
tap_ok "verify spki system-wide configuration"
# Add the public key into revoked list
cat "${SPKIPUBLICPEM}" > ${test_tmpdir}/revoked.spki.d/correct
# Check if public key is not valid anymore
if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-dir=${test_tmpdir} ${COMMIT}; then
exit 1
fi
rm -rf ${test_tmpdir}/{trusted,revoked}.spki.d
tap_ok "verify spki revoking keys mechanism"
tap_end