]> git.ipfire.org Git - pakfire.git/commitdiff
keys: Implement signature verification
authorMichael Tremer <michael.tremer@ipfire.org>
Thu, 1 Jun 2023 15:24:57 +0000 (15:24 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Thu, 1 Jun 2023 15:24:57 +0000 (15:24 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/include/pakfire/key.h
src/libpakfire/key.c
tests/libpakfire/key.c

index a083335cfd0e9e17eec63742f911cdbf14672cfe..ecc6c98bd91014ae4fb121b1e05b56bbd506bb6c 100644 (file)
@@ -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
 
index 47e546fdd09d023fbc88b137fc38d3c7dcacbb55..803045637386e5bca58742436b11118e3312c674 100644 (file)
@@ -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;
+}
index 8752d821ea9ffe97ce5ccc26f10c92e63f7fe71f..360c1df9ba6715679cbeead9c86d25d77796ade6 100644 (file)
@@ -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);