From baf6151ef014a97e1dcf02214660c7f57d799de6 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Wed, 28 Aug 2019 04:21:22 +0300 Subject: [PATCH] builtin/sign: allow to sign with keys from secret file Read keys from secret file provided by `--keys-file=` option. Signed-off-by: Denis Pynkin --- src/ostree/ot-builtin-sign.c | 109 ++++++++++++++++++++++++++++------- tests/test-signed-commit.sh | 17 +++++- 2 files changed, 103 insertions(+), 23 deletions(-) diff --git a/src/ostree/ot-builtin-sign.c b/src/ostree/ot-builtin-sign.c index 6baeb850e7..6ce3209163 100644 --- a/src/ostree/ot-builtin-sign.c +++ b/src/ostree/ot-builtin-sign.c @@ -48,7 +48,7 @@ static GOptionEntry options[] = { { "verify", 0, 0, G_OPTION_ARG_NONE, &opt_verify, "Verify signatures", NULL}, { "sign-type", 's', 0, G_OPTION_ARG_STRING, &opt_sign_name, "Signature type to use (defaults to 'ed25519')", "NAME"}, #if defined(HAVE_LIBSODIUM) - { "keys-file", 's', 0, G_OPTION_ARG_STRING, &opt_filename, "Read public key(s) from file", "NAME"}, + { "keys-file", 's', 0, G_OPTION_ARG_STRING, &opt_filename, "Read key(s) from file", "NAME"}, #endif { NULL } }; @@ -92,7 +92,7 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, commit = argv[1]; - if (!opt_verify && argc < 3) + if (!opt_filename && argc < 3) { usage_error (context, "Need at least one KEY-ID to sign with", error); goto out; @@ -174,30 +174,95 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, } } - /* Read public signatures from file */ - if (opt_verify && opt_filename) + /* Read signatures from file */ + if (opt_filename) { - g_autoptr (GVariantBuilder) builder = NULL; - g_autoptr (GVariant) options = NULL; + if (opt_verify) + { + g_autoptr (GVariantBuilder) builder = NULL; + g_autoptr (GVariant) options = NULL; - builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); - g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (opt_filename)); - options = g_variant_builder_end (builder); + builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (opt_filename)); + options = g_variant_builder_end (builder); - if (!ostree_sign_load_pk (sign, options, error)) - { - ret = FALSE; - goto out; + if (!ostree_sign_load_pk (sign, options, error)) + { + ret = FALSE; + goto out; + } + if (ostree_sign_commit_verify (sign, + repo, + resolved_commit, + cancellable, + error)) + ret = TRUE; + if (ret != TRUE) + goto out; + } /* Check via file */ + else + { /* Sign with keys from provided file */ + g_autoptr (GFile) keyfile = NULL; + g_autoptr (GFileInputStream) key_stream_in = NULL; + g_autoptr (GDataInputStream) key_data_in = NULL; + + if (!g_file_test (opt_filename, G_FILE_TEST_IS_REGULAR)) + { + g_warning ("Can't open file '%s' with keys", opt_filename); + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "File object '%s' is not a regular file", opt_filename); + goto out; + } + + keyfile = g_file_new_for_path (opt_filename); + key_stream_in = g_file_read (keyfile, NULL, error); + if (key_stream_in == NULL) + goto out; + + key_data_in = g_data_input_stream_new (G_INPUT_STREAM(key_stream_in)); + g_assert (key_data_in != NULL); + + /* Use simple file format with just a list of base64 public keys per line */ + while (TRUE) + { + gsize len = 0; + g_autofree char *line = g_data_input_stream_read_line (key_data_in, &len, NULL, error); + g_autoptr (GVariant) sk = NULL; + + if (*error != NULL) + goto out; + + if (line == NULL) + goto out; + + + if (!g_strcmp0(ostree_sign_get_name(sign), "dummy")) + { + // Just use the string as signature + sk = g_variant_new_string(line); + } + + + if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519")) + { + gsize key_len = 0; + g_autofree guchar *key = g_base64_decode (line, &key_len); + sk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); + } + + if (!ostree_sign_set_sk (sign, sk, error)) + continue; + + ret = ostree_sign_commit (sign, + repo, + resolved_commit, + cancellable, + error); + if (ret != TRUE) + goto out; + } } - if (ostree_sign_commit_verify (sign, - repo, - resolved_commit, - cancellable, - error)) - ret = TRUE; - if (ret != TRUE) - goto out; - } /* Check via file */ + } // No valid signature found if (opt_verify && (ret != TRUE)) diff --git a/tests/test-signed-commit.sh b/tests/test-signed-commit.sh index 2c54754259..ce29b1e4fc 100755 --- a/tests/test-signed-commit.sh +++ b/tests/test-signed-commit.sh @@ -23,7 +23,7 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..7" +echo "1..8" mkdir ${test_tmpdir}/repo ostree_repo_init repo --mode="archive" @@ -57,6 +57,7 @@ if ! has_libsodium; then echo "ok ed25519 signature verified # SKIP due libsodium unavailability" echo "ok multiple signing # SKIP due libsodium unavailability" echo "ok verify ed25519 keys file # SKIP due libsodium unavailability" + echo "ok sign with ed25519 keys file # SKIP due libsodium unavailability" exit 0 fi @@ -136,3 +137,17 @@ echo ${PUBLIC} >> ${PUBKEYS} ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${PUBKEYS} ${COMMIT} echo "ok verify ed25519 keys file" + +# Check ed25519 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.ed25519)" +echo "${SECRET}" > ${KEYFILE} +# Sign +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=ed25519 --keys-file=${KEYFILE} ${COMMIT} +# Verify +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${PUBKEYS} ${COMMIT} +echo "ok sign with ed25519 keys file" +