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) {
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;
// 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) {
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);
r = 0;
ERROR:
- if (pctx)
- EVP_PKEY_CTX_free(pctx);
if (mdctx)
EVP_MD_CTX_free(mdctx);
}
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;
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;
+}
#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;
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));
// 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;
FAIL:
if (key)
pakfire_key_unref(key);
+ if (f)
+ fclose(f);
return r;
}
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);