From dbce36dfd7f2c041b9fd558e97184911bb627f0e Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Thu, 1 Jun 2023 15:24:57 +0000 Subject: [PATCH] keys: Implement signature verification Signed-off-by: Michael Tremer --- src/libpakfire/include/pakfire/key.h | 4 +- src/libpakfire/key.c | 187 +++++++++++++++++++++++++-- tests/libpakfire/key.c | 25 +++- 3 files changed, 195 insertions(+), 21 deletions(-) diff --git a/src/libpakfire/include/pakfire/key.h b/src/libpakfire/include/pakfire/key.h index a083335cf..ecc6c98bd 100644 --- a/src/libpakfire/include/pakfire/key.h +++ b/src/libpakfire/include/pakfire/key.h @@ -59,7 +59,9 @@ int pakfire_key_import_from_string(struct pakfire_key** key, struct pakfire* pakfire, const char* data, const size_t length); int pakfire_key_sign(struct pakfire_key* key, - FILE* f, const char* data, const size_t length, const char* comment); + FILE* f, const void* data, const size_t length, const char* comment); +int pakfire_key_verify(struct pakfire_key* key, + FILE* f, const void* data, const size_t length); #endif diff --git a/src/libpakfire/key.c b/src/libpakfire/key.c index 47e546fdd..803045637 100644 --- a/src/libpakfire/key.c +++ b/src/libpakfire/key.c @@ -85,6 +85,10 @@ struct pakfire_key_signature { unsigned char signature[64]; }; +static int pakfire_key_id_equals(const pakfire_key_id* id1, const pakfire_key_id* id2) { + return !memcmp(*id1, *id2, sizeof(*id1)); +} + static int pakfire_key_create(struct pakfire_key** key, struct pakfire* pakfire, const pakfire_key_algo_t algo, const pakfire_key_id id, EVP_PKEY* pkey) { if (!pkey) { @@ -768,7 +772,6 @@ ERROR: static int __pakfire_key_sign(struct pakfire_key* key, struct pakfire_key_signature* signature, const void* data, const size_t length) { - EVP_PKEY_CTX* pctx = NULL; EVP_MD_CTX* mdctx = NULL; char error[ERROR_MAX]; int r; @@ -786,14 +789,6 @@ static int __pakfire_key_sign(struct pakfire_key* key, // Set the key ID memcpy(signature->key_id, key->id, sizeof(signature->key_id)); - // Create a signing context - pctx = EVP_PKEY_CTX_new(key->pkey, NULL); - if (!pctx) { - ERROR(key->pakfire, "Could not initialize the signing context: %m\n"); - r = 1; - goto ERROR; - } - // Create a message digest context mdctx = EVP_MD_CTX_new(); if (!mdctx) { @@ -802,8 +797,13 @@ static int __pakfire_key_sign(struct pakfire_key* key, goto ERROR; } - // Connect the two contexts - EVP_MD_CTX_set_pkey_ctx(mdctx, pctx); + // Setup the context + r = EVP_DigestSignInit(mdctx, NULL, NULL, NULL, key->pkey); + if (r < 1) { + ERROR(key->pakfire, "Could not setup context\n"); + r = 1; + goto ERROR; + } size_t signature_length = sizeof(signature->signature); @@ -821,8 +821,6 @@ static int __pakfire_key_sign(struct pakfire_key* key, r = 0; ERROR: - if (pctx) - EVP_PKEY_CTX_free(pctx); if (mdctx) EVP_MD_CTX_free(mdctx); @@ -830,7 +828,7 @@ ERROR: } int pakfire_key_sign(struct pakfire_key* key, - FILE* f, const char* data, const size_t length, const char* comment) { + FILE* f, const void* data, const size_t length, const char* comment) { struct pakfire_key_signature signature = { 0 }; char* s = NULL; int r; @@ -872,3 +870,164 @@ ERROR: return r; } + +static int pakfire_key_read_signature(struct pakfire_key* key, + struct pakfire_key_signature* signature, FILE* f) { + void* buffer = NULL; + size_t buffer_length = 0; + int r; + + char* line = NULL; + size_t length = 0; + size_t lineno = 0; + + for (;;) { + ssize_t bytes_read = getline(&line, &length, f); + if (bytes_read < 0) + break; + + // Increment the line counter + lineno++; + + switch (lineno) { + // The first line must start with "untrusted comment:" + case 1: + if (!pakfire_string_startswith(line, "untrusted comment:")) { + ERROR(key->pakfire, "The first line must start with 'untrusted comment:'\n"); + errno = EINVAL; + r = 1; + goto ERROR; + } + break; + + // The second line should hold the signature + case 2: + // Decode the key + r = pakfire_b64decode(key->pakfire, &buffer, &buffer_length, line); + if (r) { + ERROR(key->pakfire, "Could not decode the signature: %m\n"); + errno = EINVAL; + r = 1; + goto ERROR; + } + + // What kind of signature do we have? + switch (buffer_length) { + case sizeof(*signature): + // Copy the buffer to the signature + memcpy(signature, buffer, sizeof(*signature)); + + // Check if we support the signature type + if (signature->sig_algo[0] != 'E' || signature->sig_algo[1] != 'd') { + ERROR(key->pakfire, "Unknown signature type\n"); + errno = ENOTSUP; + r = 1; + goto ERROR; + } + break; + + default: + ERROR(key->pakfire, "Unknown signature type\n"); + errno = ENOTSUP; + r = 1; + goto ERROR; + } + break; + + // Ignore any further data + default: + break; + } + } + +ERROR: + if (buffer) + free(buffer); + if (line) + free(line); + + return r; +} + +static int pakfire_key_verify_signature(struct pakfire_key* key, + const struct pakfire_key_signature* signature, const void* data, const size_t length) { + EVP_MD_CTX* mdctx = NULL; + int r; + + DEBUG(key->pakfire, "Verifying signature...\n"); + + // Check the key ID + if (!pakfire_key_id_equals(&key->id, &signature->key_id)) { + ERROR(key->pakfire, "The signature has been created with a different key\n"); + errno = EBADMSG; + r = 1; + goto ERROR; + } + + // Create message digest context + mdctx = EVP_MD_CTX_new(); + if (!mdctx) { + ERROR(key->pakfire, "Could not create the message digest context\n"); + r = 1; + goto ERROR; + } + + // Setup the context for verification + r = EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, key->pkey); + if (r < 1) { + ERROR(key->pakfire, "Could not setup the verification context\n"); + r = 1; + goto ERROR; + } + + // Verify the signature + r = EVP_DigestVerify(mdctx, signature->signature, sizeof(signature->signature), + data, length); + switch (r) { + // Fail + case 0: + ERROR(key->pakfire, "Signature verification failed\n"); + errno = EBADMSG; + r = 1; + break; + + // Success + case 1: + DEBUG(key->pakfire, "Signature verification successful\n"); + r = 0; + break; + + // Error + default: + ERROR(key->pakfire, "Could not perform signature verification\n"); + r = 1; + goto ERROR; + } + +ERROR: + if (mdctx) + EVP_MD_CTX_free(mdctx); + + return r; +} + +int pakfire_key_verify(struct pakfire_key* key, FILE* f, const void* data, const size_t length) { + struct pakfire_key_signature signature = { 0 }; + int r; + + // Read the signature + r = pakfire_key_read_signature(key, &signature, f); + if (r) { + ERROR(key->pakfire, "Could not read signature: %m\n"); + return r; + } + + // Verify signature + r = pakfire_key_verify_signature(key, &signature, data, length); + if (r) { + ERROR(key->pakfire, "Could not verify signature: %m\n"); + return r; + } + + return 0; +} diff --git a/tests/libpakfire/key.c b/tests/libpakfire/key.c index 8752d821e..360c1df9b 100644 --- a/tests/libpakfire/key.c +++ b/tests/libpakfire/key.c @@ -26,6 +26,9 @@ #include "../testsuite.h" +const char DATA[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const size_t DATA_LENGTH = sizeof(DATA); + static int test_generate(const struct test* t) { struct pakfire_key* key = NULL; int r = EXIT_FAILURE; @@ -52,16 +55,17 @@ FAIL: return r; } -static int test_sign(const struct test* t) { +static int test_sign_and_verify(const struct test* t) { struct pakfire_key* key = NULL; int r = EXIT_FAILURE; - const char* data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - const size_t length = strlen(data); - + FILE* f = NULL; char* signature = NULL; size_t signature_length = 0; + // Create a file handle to write the signature to + ASSERT(f = test_mktemp(NULL)); + // Generate a new key using ed25519 ASSERT_SUCCESS(pakfire_key_generate(&key, t->pakfire, PAKFIRE_KEY_ALGO_ED25519)); @@ -71,7 +75,14 @@ static int test_sign(const struct test* t) { // Write the private key to the console ASSERT_SUCCESS(pakfire_key_export(key, stdout, PAKFIRE_KEY_EXPORT_MODE_PRIVATE)); - ASSERT_SUCCESS(pakfire_key_sign(key, stdout, data, length, "UNTRUSTED COMMENT")); + // Sign! + ASSERT_SUCCESS(pakfire_key_sign(key, f, DATA, DATA_LENGTH, "UNTRUSTED COMMENT")); + + // Rewind the file + rewind(f); + + // Verify the signature + ASSERT_SUCCESS(pakfire_key_verify(key, f, DATA, DATA_LENGTH)); // Everything passed r = EXIT_SUCCESS; @@ -79,6 +90,8 @@ static int test_sign(const struct test* t) { FAIL: if (key) pakfire_key_unref(key); + if (f) + fclose(f); return r; } @@ -137,7 +150,7 @@ FAIL: int main(int argc, const char* argv[]) { testsuite_add_test(test_generate); - testsuite_add_test(test_sign); + testsuite_add_test(test_sign_and_verify); testsuite_add_test(test_import_public); testsuite_add_test(test_import_secret); -- 2.47.3