#############################################################################*/
#include <errno.h>
-#include <gpgme.h>
#include <linux/limits.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
+// OpenSSL
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/rand.h>
+
#include <pakfire/constants.h>
#include <pakfire/i18n.h>
#include <pakfire/key.h>
#include <pakfire/string.h>
#include <pakfire/util.h>
+// Size of the buffer to allocate for error messages
+#define ERROR_MAX 1024
+
+typedef unsigned char pakfire_key_id[8];
+
struct pakfire_key {
struct pakfire* pakfire;
int nrefs;
- gpgme_key_t gpgkey;
+ // Algorithm
+ pakfire_key_algo_t algo;
+
+ // Key ID
+ pakfire_key_id id;
+
+ // The public (or private) key
+ EVP_PKEY* pkey;
+};
+
+struct pakfire_key_public_key {
+ unsigned char sig_algo[2];
+ pakfire_key_id id;
+ unsigned char pubkey[32];
+};
+
+struct pakfire_key_private_key {
+ unsigned char sig_algo[2];
+ unsigned char kdf_algo[2];
+ uint32_t kdf_rounds;
+ unsigned char kdf_salt[16];
+ unsigned char checksum[8];
+ pakfire_key_id id;
+ struct {
+ unsigned char secret[32];
+ unsigned char public[32];
+ } keys;
+};
+
+struct pakfire_key_signature {
+ unsigned char sig_algo[2];
+ pakfire_key_id key_id;
+ unsigned char signature[64];
};
-int pakfire_key_create(struct pakfire_key** key, struct pakfire* pakfire, gpgme_key_t gpgkey) {
- if (!gpgkey) {
+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) {
errno = EINVAL;
return 1;
}
k->pakfire = pakfire_ref(pakfire);
k->nrefs = 1;
+ // Store the algorithm
+ switch (algo) {
+ case PAKFIRE_KEY_ALGO_ED25519:
+ k->algo = algo;
+ break;
+
+ default:
+ ERROR(pakfire, "Unsupported key algorithm %d\n", algo);
+ errno = ENOTSUP;
+ goto ERROR;
+ }
+
+ // Store the key ID
+ memcpy(k->id, id, sizeof(k->id));
+
// Keep a reference to this key
- gpgme_key_ref(gpgkey);
- k->gpgkey = gpgkey;
+ EVP_PKEY_up_ref(pkey);
+ k->pkey = pkey;
*key = k;
return 0;
+
+ERROR:
+ pakfire_key_unref(k);
+
+ return 1;
}
static void pakfire_key_free(struct pakfire_key* key) {
- gpgme_key_unref(key->gpgkey);
+ // Free the key
+ if (key->pkey)
+ EVP_PKEY_free(key->pkey);
+
pakfire_unref(key->pakfire);
free(key);
}
pakfire_key_free(key);
}
-static int pakfire_find_key(struct pakfire_key** key, struct pakfire* pakfire, const char* fingerprint) {
- if (!fingerprint) {
- errno = EINVAL;
- return 1;
- }
-
- // Reset key
- *key = NULL;
+PAKFIRE_EXPORT const char* pakfire_key_get_algo(struct pakfire_key* key) {
+ switch (key->algo) {
+ case PAKFIRE_KEY_ALGO_ED25519:
+ return "Ed255919";
- // Fetch GPGME context
- gpgme_ctx_t gpgctx = pakfire_get_gpgctx(pakfire);
- if (!gpgctx)
- return 1;
+ case PAKFIRE_KEY_ALGO_NULL:
+ break;
+ }
- DEBUG(pakfire, "Seaching for key with fingerprint %s\n", fingerprint);
+ return NULL;
+}
- gpgme_key_t gpgkey = NULL;
+PAKFIRE_EXPORT int pakfire_key_generate(struct pakfire_key** key, struct pakfire* pakfire,
+ const pakfire_key_algo_t algo) {
+ EVP_PKEY* pkey = NULL;
+ EVP_PKEY_CTX* pctx = NULL;
+ pakfire_key_id key_id;
+ char error[ERROR_MAX];
+ int id;
int r;
- gpgme_error_t error = gpgme_get_key(gpgctx, fingerprint, &gpgkey, 0);
- switch (gpg_error(error)) {
- // Create a key object if we found something
- case GPG_ERR_NO_ERROR:
- r = pakfire_key_create(key, pakfire, gpgkey);
- gpgme_key_unref(gpgkey);
- if (r)
- return r;
+ switch (algo) {
+ case PAKFIRE_KEY_ALGO_ED25519:
+ id = EVP_PKEY_ED25519;
break;
- // Nothing found
- case GPG_ERR_EOF:
- break;
+ default:
+ ERROR(pakfire, "Invalid key algorithm %d\n", algo);
+ errno = EINVAL;
+ return 1;
}
- return 0;
-}
+ // Generate a random key ID
+ r = RAND_bytes(key_id, sizeof(key_id));
+ if (r < 0) {
+ ERROR(pakfire, "Could not generate the key ID\n");
+ r = 1;
+ goto ERROR;
+ }
-PAKFIRE_EXPORT struct pakfire_key* pakfire_key_get(struct pakfire* pakfire, const char* fingerprint) {
- struct pakfire_key* key = NULL;
+ // Setup the context
+ pctx = EVP_PKEY_CTX_new_id(id, NULL);
+ if (!pctx) {
+ ERROR(pakfire, "Could not allocate the OpenSSL context: %m\n");
+ r = 1;
+ goto ERROR;
+ }
- int r = pakfire_find_key(&key, pakfire, fingerprint);
- if (r)
- return NULL;
+ // Prepare the context for key generation
+ r = EVP_PKEY_keygen_init(pctx);
+ if (r < 1) {
+ ERR_error_string_n(r, error, sizeof(error));
- return key;
-}
+ ERROR(pakfire, "Could not prepare the context: %s\n", error);
+ r = 1;
+ goto ERROR;
+ }
-PAKFIRE_EXPORT int pakfire_key_delete(struct pakfire_key* key) {
- gpgme_ctx_t gpgctx = pakfire_get_gpgctx(key->pakfire);
+ // Generate the key
+ r = EVP_PKEY_keygen(pctx, &pkey);
+ if (r < 1) {
+ ERR_error_string_n(r, error, sizeof(error));
- int r = 0;
- gpgme_error_t error = gpgme_op_delete(gpgctx, key->gpgkey, 1);
- if (error != GPG_ERR_NO_ERROR)
+ ERROR(pakfire, "Could not generate the key: %s\n", error);
r = 1;
+ goto ERROR;
+ }
- return r;
-}
+ // Create a key object
+ r = pakfire_key_create(key, pakfire, algo, key_id, pkey);
+ if (r)
+ goto ERROR;
-PAKFIRE_EXPORT const char* pakfire_key_get_fingerprint(struct pakfire_key* key) {
- return key->gpgkey->fpr;
-}
+ // Success
+ r = 0;
-PAKFIRE_EXPORT const char* pakfire_key_get_uid(struct pakfire_key* key) {
- if (key->gpgkey->uids)
- return key->gpgkey->uids->uid;
+ERROR:
+ if (pctx)
+ EVP_PKEY_CTX_free(pctx);
+ if (pkey)
+ EVP_PKEY_free(pkey);
- return NULL;
+ return r;
}
-PAKFIRE_EXPORT const char* pakfire_key_get_name(struct pakfire_key* key) {
- if (key->gpgkey->uids)
- return key->gpgkey->uids->name;
+/*
+ Import
+*/
- return NULL;
-}
-
-PAKFIRE_EXPORT const char* pakfire_key_get_email(struct pakfire_key* key) {
- if (key->gpgkey->uids)
- return key->gpgkey->uids->email;
+static int pakfire_key_import_secret_key(struct pakfire_key** key,
+ struct pakfire* pakfire, const struct pakfire_key_private_key* buffer) {
+ const pakfire_key_algo_t algo = PAKFIRE_KEY_ALGO_ED25519;
+ EVP_PKEY* pkey = NULL;
+ char error[ERROR_MAX];
+ int r;
- return NULL;
-}
+ unsigned char checksum[64];
+ unsigned int length = sizeof(checksum);
-PAKFIRE_EXPORT const char* pakfire_key_get_pubkey_algo(struct pakfire_key* key) {
- if (!key->gpgkey->subkeys)
- return NULL;
+ // Check for input
+ if (!buffer) {
+ errno = EINVAL;
+ return 1;
+ }
- switch (key->gpgkey->subkeys->pubkey_algo) {
- case GPGME_PK_RSA:
- case GPGME_PK_RSA_E:
- case GPGME_PK_RSA_S:
- return "RSA";
+ // Check the signature algorithm
+ if (buffer->sig_algo[0] != 'E' || buffer->sig_algo[1] != 'd') {
+ ERROR(pakfire, "Unsupported signature algorithm\n");
+ errno = ENOTSUP;
+ r = 1;
+ goto ERROR;
+ }
- case GPGME_PK_DSA:
- return "DSA";
+ // Check the KDF algorithm
+ if (buffer->kdf_algo[0] != 'B' || buffer->kdf_algo[1] != 'K') {
+ ERROR(pakfire, "Unsupported KDF algorithm\n");
+ errno = ENOTSUP;
+ r = 1;
+ goto ERROR;
+ }
- case GPGME_PK_ECDSA:
- return "ECDSA";
+ // We don't support encrypted keys here
+ if (buffer->kdf_rounds) {
+ ERROR(pakfire, "Encrypted keys are not supported\n");
+ errno = ENOTSUP;
+ r = 1;
+ goto ERROR;
+ }
- case GPGME_PK_ECDH:
- return "ECDH";
+ // Compute a SHA512 checksum over the key material
+ r = EVP_Digest(&buffer->keys, sizeof(buffer->keys), checksum, &length, EVP_sha512(), NULL);
+ if (r < 0) {
+ ERROR(pakfire, "Could not compute the checksum: %m\n");
+ r = 1;
+ goto ERROR;
+ }
- case GPGME_PK_ECC:
- return "ECC";
+ // Compare the checksum
+ if (memcmp(buffer->checksum, checksum, sizeof(buffer->checksum)) != 0) {
+ ERROR(pakfire, "Checksum mismatch\n");
+ r = 1;
+ goto ERROR;
+ }
- case GPGME_PK_EDDSA:
- return "EDDSA";
+ // Load the private key
+ pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL,
+ buffer->keys.secret, sizeof(buffer->keys.secret));
+ if (!pkey) {
+ ERR_error_string_n(ERR_get_error(), error, sizeof(error));
- case GPGME_PK_ELG:
- case GPGME_PK_ELG_E:
- return "ELG";
+ ERROR(pakfire, "Could not load secret key: %s\n", error);
+ r = 1;
+ goto ERROR;
}
- return NULL;
-}
+ // Create a new key object
+ r = pakfire_key_create(key, pakfire, algo, buffer->id, pkey);
+ if (r)
+ goto ERROR;
-PAKFIRE_EXPORT size_t pakfire_key_get_pubkey_length(struct pakfire_key* key) {
- if (key->gpgkey->subkeys)
- return key->gpgkey->subkeys->length;
+ERROR:
+ if (pkey)
+ EVP_PKEY_free(pkey);
return 0;
}
-PAKFIRE_EXPORT int pakfire_key_has_secret(struct pakfire_key* key) {
- if (key->gpgkey)
- return key->gpgkey->secret;
+static int pakfire_key_import_public_key(struct pakfire_key** key,
+ struct pakfire* pakfire, const struct pakfire_key_public_key* buffer) {
+ const pakfire_key_algo_t algo = PAKFIRE_KEY_ALGO_ED25519;
+ EVP_PKEY* pkey = NULL;
+ char error[ERROR_MAX];
+ int r;
- return 0;
-}
+ // Check for input
+ if (!buffer) {
+ errno = EINVAL;
+ return 1;
+ }
-PAKFIRE_EXPORT time_t pakfire_key_get_created(struct pakfire_key* key) {
- if (key->gpgkey->subkeys)
- return key->gpgkey->subkeys->timestamp;
+ // Check the signature algorithm
+ if (buffer->sig_algo[0] != 'E' || buffer->sig_algo[1] != 'd') {
+ ERROR(pakfire, "Unsupported signature algorithm\n");
+ errno = ENOTSUP;
+ r = 1;
+ goto ERROR;
+ }
- return 0;
-}
+ // Load the public key
+ pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL,
+ buffer->pubkey, sizeof(buffer->pubkey));
+ if (!pkey) {
+ ERR_error_string_n(ERR_get_error(), error, sizeof(error));
-PAKFIRE_EXPORT time_t pakfire_key_get_expires(struct pakfire_key* key) {
- if (key->gpgkey->subkeys)
- return key->gpgkey->subkeys->expires;
+ ERROR(pakfire, "Could not load public key: %s\n", error);
+ r = 1;
+ goto ERROR;
+ }
- return 0;
-}
+ // Create a new key object
+ r = pakfire_key_create(key, pakfire, algo, buffer->id, pkey);
+ if (r)
+ goto ERROR;
-PAKFIRE_EXPORT int pakfire_key_is_revoked(struct pakfire_key* key) {
- if (key->gpgkey->subkeys)
- return key->gpgkey->subkeys->revoked;
+ERROR:
+ if (pkey)
+ EVP_PKEY_free(pkey);
- return 0;
+ return r;
}
-static int pakfire_key_write_to_keystore(struct pakfire_key* key) {
- // Fetch keystore path
- const char* keystore_path = pakfire_get_keystore_path(key->pakfire);
- if (!keystore_path)
- return 1;
-
- // Fetch fingerprint
- const char* fpr = pakfire_key_get_fingerprint(key);
- if (!fpr)
- return 1;
+PAKFIRE_EXPORT int pakfire_key_import(struct pakfire_key** key,
+ struct pakfire* pakfire, FILE* f) {
+ BIO* bio = NULL;
+ BIO* b64 = NULL;
+ char buffer[4096];
+ size_t bytes_read = 0;
+ int r;
- char path[PATH_MAX];
+ // Setup the base64 decoder
+ b64 = BIO_new(BIO_f_base64());
+ if (!b64) {
+ ERROR(pakfire, "Could not setup the base64 decoder\n");
+ r = 1;
+ goto ERROR;
+ }
- // Make path
- int r = pakfire_string_format(path, "%s/%s.key", keystore_path, fpr);
- if (r)
- return r;
+ bio = BIO_new_fp(f, BIO_NOCLOSE);
+ if (!bio) {
+ ERROR(pakfire, "Could not open BIO\n");
+ r = 1;
+ goto ERROR;
+ }
- // Create parent directory
- r = pakfire_mkparentdir(path, 0700);
- if (r)
- return r;
+ BIO_push(b64, bio);
- // Create file
- FILE* f = fopen(path, "w");
- if (!f) {
- ERROR(key->pakfire, "Could not open %s for writing: %m\n", path);
- return 1;
+ // Try to read everything into the buffer
+ r = BIO_read_ex(b64, buffer, sizeof(buffer), &bytes_read);
+ if (r < 0) {
+ ERROR(pakfire, "Could not read data\n");
+ r = 1;
+ goto ERROR;
}
- // Make files with secret keys non-world-readable
- if (pakfire_key_has_secret(key)) {
- r = chmod(path, 0600);
- if (r) {
- ERROR(key->pakfire, "Could not chmod %s: %m\n", path);
- fclose(f);
- return r;
- }
- }
+ const struct pakfire_key_private_key* private_key =
+ (const struct pakfire_key_private_key*)buffer;
+ const struct pakfire_key_public_key* public_key =
+ (const struct pakfire_key_public_key*)buffer;
- // Write key to file
- r = pakfire_key_export(key, f, 0);
- if (r) {
- ERROR(key->pakfire, "Could not export key %s: %m\n", fpr);
- unlink(path);
+ // Try to find what we have read
+ switch (bytes_read) {
+
+ // Import a private key
+ case sizeof(*private_key):
+ r = pakfire_key_import_secret_key(key, pakfire, private_key);
+ break;
+
+ // Import a public key
+ case sizeof(*public_key):
+ r = pakfire_key_import_public_key(key, pakfire, public_key);
+ break;
+
+ default:
+ ERROR(pakfire, "Unsupported key type\n");
+ r = 1;
+ goto ERROR;
}
- // Close file
- fclose(f);
+ERROR:
+ if (b64)
+ BIO_free(b64);
+ if (bio)
+ BIO_free(bio);
return r;
}
-PAKFIRE_EXPORT int pakfire_key_generate(struct pakfire_key** key, struct pakfire* pakfire,
- const char* algo, const char* userid) {
+int pakfire_key_import_from_string(struct pakfire_key** key,
+ struct pakfire* pakfire, const char* data, const size_t length) {
+ FILE* f = NULL;
int r;
- // Check input
- if (!algo || !userid) {
- errno = EINVAL;
- return 1;
+ // Map the data to a file
+ f = fmemopen((char*)data, length, "r");
+ if (!f) {
+ ERROR(pakfire, "Could not map the key to file: %m\n");
+ r = 1;
+ goto ERROR;
}
- // Fetch GPGME context
- gpgme_ctx_t gpgctx = pakfire_get_gpgctx(pakfire);
- if (!gpgctx)
- return 1;
+ // Import the key
+ r = pakfire_key_import(key, pakfire, f);
- const unsigned int flags =
- // Key should be able to be used to sign
- GPGME_CREATE_SIGN |
- // Don't set a password
- GPGME_CREATE_NOPASSWD |
- // The key should never expire
- GPGME_CREATE_NOEXPIRE;
+ERROR:
+ if (f)
+ fclose(f);
- // Generate the key
- gpgme_error_t error = gpgme_op_createkey(gpgctx, userid,
- algo, 0, 0, NULL, flags);
-
- if (error != GPG_ERR_NO_ERROR) {
- switch (gpg_err_code(error)) {
- case GPG_ERR_USER_ID_EXISTS:
- case GPG_ERR_NAME_EXISTS:
- errno = EINVAL;
- break;
-
- default:
- break;
- }
+ return r;
+}
+
+static int pakfire_key_get_public_key(struct pakfire_key* key,
+ unsigned char* buffer, const size_t length) {
+ char error[ERROR_MAX];
+ int r;
- ERROR(pakfire, "%s\n", gpgme_strerror(error));
+ size_t l = length;
+
+ // Extract the raw public key
+ r = EVP_PKEY_get_raw_public_key(key->pkey, buffer, &l);
+ if (r < 0) {
+ ERR_error_string_n(ERR_get_error(), error, sizeof(error));
+
+ ERROR(key->pakfire, "Could not extract the public key: %s\n", error);
return 1;
}
- // Retrieve the result
- gpgme_genkey_result_t result = gpgme_op_genkey_result(gpgctx);
-
- // Retrieve the key by its fingerprint
- r = pakfire_find_key(key, pakfire, result->fpr);
- if (r)
- return r;
+ if (l > length) {
+ ERROR(key->pakfire, "The buffer was too small to write the public key\n");
+ errno = ENOBUFS;
+ return 1;
+ }
- // Store the key in the keystore
- return pakfire_key_write_to_keystore(*key);
+ return 0;
}
-static int pakfire_key_data(struct pakfire_key* key, char** buffer, size_t* length,
- const pakfire_key_export_mode_t mode) {
- char* output = NULL;
+static int pakfire_key_get_secret_key(struct pakfire_key* key,
+ unsigned char* buffer, const size_t length) {
+ char error[ERROR_MAX];
+ int r;
- gpgme_ctx_t gpgctx = pakfire_get_gpgctx(key->pakfire);
- if (!gpgctx)
- return 1;
+ size_t l = length;
- gpgme_export_mode_t gpgmode = 0;
- switch (mode) {
- case PAKFIRE_KEY_EXPORT_MODE_SECRET:
- gpgmode |= GPGME_EXPORT_MODE_SECRET;
- break;
+ // Extract the raw secret key
+ r = EVP_PKEY_get_raw_private_key(key->pkey, buffer, &l);
+ if (r < 0) {
+ ERR_error_string_n(ERR_get_error(), error, sizeof(error));
- default:
- break;
+ ERROR(key->pakfire, "Could not extract the secret key: %s\n", error);
+ return 1;
}
- const char* fingerprint = pakfire_key_get_fingerprint(key);
-
- DEBUG(key->pakfire, "Exporting key %s\n", fingerprint);
+ if (l > length) {
+ ERROR(key->pakfire, "The buffer was too small to write the secret key\n");
+ errno = ENOBUFS;
+ return 1;
+ }
- gpgme_data_t data = NULL;
- int r = 1;
+ return 0;
+}
- // Initialize the buffer
- gpgme_error_t e = gpgme_data_new(&data);
- if (gpg_err_code(e) != GPG_ERR_NO_ERROR)
- goto ERROR;
+/*
+ Export
+*/
- // Encode output as ASCII
- e = gpgme_data_set_encoding(data, GPGME_DATA_ENCODING_ARMOR);
- if (gpg_err_code(e) != GPG_ERR_NO_ERROR)
- goto ERROR;
+static int pakfire_key_export_private_key(struct pakfire_key* key,
+ struct pakfire_key_private_key* buffer) {
+ unsigned char checksum[64];
+ unsigned int length = sizeof(checksum);
+ int r;
- // Copy the key to the buffer
- e = gpgme_op_export(gpgctx, fingerprint, gpgmode, data);
- if (gpg_err_code(e) != GPG_ERR_NO_ERROR)
- goto ERROR;
+ // Write the algorithm
+ switch (key->algo) {
+ case PAKFIRE_KEY_ALGO_ED25519:
+ // Signature Algortihm
+ buffer->sig_algo[0] = 'E';
+ buffer->sig_algo[1] = 'd';
- // Fetch data from buffer
- output = gpgme_data_release_and_get_mem(data, length);
+ // KDF Algorithm
+ buffer->kdf_algo[0] = 'B';
+ buffer->kdf_algo[1] = 'K';
+ break;
- // Reset data so it won't be freed again
- data = NULL;
+ default:
+ ERROR(key->pakfire, "Unknown algorithm\n");
+ return 1;
+ }
- // Allocate buffer
- *buffer = malloc(*length);
- if (!*buffer) {
- r = 1;
- goto ERROR;
+ // Generate a salt
+ r = RAND_bytes(buffer->kdf_salt, sizeof(buffer->kdf_salt));
+ if (r < 1) {
+ ERROR(key->pakfire, "Could not generate salt\n");
+ return 1;
}
- // Copy the output buffer
- memcpy(*buffer, output, *length);
+ // Copy the key ID
+ memcpy(buffer->id, key->id, sizeof(buffer->id));
- // Success
- r = 0;
+ // Write the public key
+ r = pakfire_key_get_public_key(key, buffer->keys.public, sizeof(buffer->keys.public));
+ if (r) {
+ ERROR(key->pakfire, "Could not export the public key: %m\n");
+ return r;
+ }
-ERROR:
- if (output)
- gpgme_free(output);
- if (data)
- gpgme_data_release(data);
+ // Write the secret key
+ r = pakfire_key_get_secret_key(key, buffer->keys.secret, sizeof(buffer->keys.secret));
+ if (r) {
+ ERROR(key->pakfire, "Could not export the secret key: %m\n");
+ return r;
+ }
- return r;
-}
+ // Compute a SHA512 checksum over the key material
+ r = EVP_Digest(&buffer->keys, sizeof(buffer->keys), checksum, &length, EVP_sha512(), NULL);
+ if (r < 0) {
+ ERROR(key->pakfire, "Could not compute the checksum: %m\n");
+ return 1;
+ }
-PAKFIRE_EXPORT int pakfire_key_get_public_key(struct pakfire_key* key,
- char** buffer, size_t* length) {
- // Fetch the public key
- return pakfire_key_data(key, buffer, length, PAKFIRE_KEY_EXPORT_MODE_PUBLIC);
-}
+ // Copy the first couple of bytes of the checksum
+ memcpy(buffer->checksum, checksum, sizeof(buffer->checksum));
-PAKFIRE_EXPORT int pakfire_key_get_secret_key(struct pakfire_key* key,
- char** buffer, size_t* length) {
- // Fetch the secret key
- return pakfire_key_data(key, buffer, length, PAKFIRE_KEY_EXPORT_MODE_SECRET);
+ // Success!
+ return 0;
}
-PAKFIRE_EXPORT int pakfire_key_export(struct pakfire_key* key, FILE* f,
- pakfire_key_export_mode_t mode) {
- char* buffer = NULL;
- size_t length = 0;
+static int pakfire_key_export_public_key(struct pakfire_key* key,
+ struct pakfire_key_public_key* buffer) {
int r;
- // Check input
- if (!f) {
- errno = EINVAL;
- return 1;
- }
-
- r = pakfire_key_data(key, &buffer, &length, mode);
- if (r)
- return r;
+ // Write the algorithm
+ switch (key->algo) {
+ case PAKFIRE_KEY_ALGO_ED25519:
+ buffer->sig_algo[0] = 'E';
+ buffer->sig_algo[1] = 'd';
+ break;
- // Write key to file
- size_t bytes_written = fwrite(buffer, 1, length, f);
- if (bytes_written < length) {
- r = 1;
- goto ERROR;
+ default:
+ ERROR(key->pakfire, "Unknown algorithm\n");
+ return 1;
}
- // Flush f
- r = fflush(f);
- if (r)
- goto ERROR;
-
- // Success
- r = 0;
+ // Copy the key ID
+ memcpy(buffer->id, key->id, sizeof(buffer->id));
-ERROR:
- if (buffer)
- gpgme_free(buffer);
+ // Write the public key
+ r = pakfire_key_get_public_key(key, buffer->pubkey, sizeof(buffer->pubkey));
+ if (r) {
+ ERROR(key->pakfire, "Could not export the public key: %m\n");
+ return r;
+ }
- return r;
+ // Success!
+ return 0;
}
-PAKFIRE_EXPORT int pakfire_key_import(struct pakfire* pakfire, FILE* f,
- struct pakfire_key*** keys) {
- gpgme_data_t data;
- int r = 1;
-
- if (!f) {
- errno = EINVAL;
- return 1;
- }
+PAKFIRE_EXPORT int pakfire_key_export(struct pakfire_key* key, FILE* f,
+ const pakfire_key_export_mode_t mode) {
+ struct pakfire_key_public_key public_key = { 0 };
+ struct pakfire_key_private_key private_key = { 0 };
+ int r;
- // Reset keys
- if (keys)
- *keys = NULL;
+ BIO* bio = NULL;
+ BIO* b64 = NULL;
- // Fetch GPGME context
- gpgme_ctx_t gpgctx = pakfire_get_gpgctx(pakfire);
- if (!gpgctx)
- return 1;
+ // Setup the base64 encoder
+ b64 = BIO_new(BIO_f_base64());
+ if (!b64) {
+ ERROR(key->pakfire, "Could not setup the base64 encoder\n");
+ r = 1;
+ goto ERROR;
+ }
- gpgme_import_result_t result = NULL;
+ // Disable line breaks and a trailing newline
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
- // Import key data
- gpgme_error_t e = gpgme_data_new_from_stream(&data, f);
- if (gpg_error(e) != GPG_ERR_NO_ERROR)
+ bio = BIO_new_fp(f, BIO_NOCLOSE);
+ if (!bio) {
+ ERROR(key->pakfire, "Could not open BIO\n");
+ r = 1;
goto ERROR;
+ }
- // Try importing the key(s)
- e = gpgme_op_import(gpgctx, data);
-
- switch (gpg_error(e)) {
- // Everything went fine
- case GPG_ERR_NO_ERROR:
- result = gpgme_op_import_result(gpgctx);
+ BIO_push(b64, bio);
- // Keep the result
- gpgme_result_ref(result);
+ switch (mode) {
+ case PAKFIRE_KEY_EXPORT_MODE_PUBLIC:
+ r = pakfire_key_export_public_key(key, &public_key);
+ if (r)
+ goto ERROR;
- // Did we import any keys?
- gpgme_import_status_t status = result->imports;
- if (!status) {
- errno = ENOENT;
+ // Write the output
+ r = BIO_write(b64, &public_key, sizeof(public_key));
+ if (r < 0) {
+ ERROR(key->pakfire, "Could not write the public key\n");
+ r = 1;
goto ERROR;
}
+ break;
- DEBUG(pakfire, "Keys considered = %d\n", result->considered);
- DEBUG(pakfire, "Keys imported = %d\n", result->imported);
- DEBUG(pakfire, "Keys not imported = %d\n", result->not_imported);
-
- if (keys) {
- // Allocate array
- *keys = calloc(result->imported + 1, sizeof(**keys));
- if (!*keys)
- goto ERROR;
-
- struct pakfire_key* key = NULL;
-
- // Retrieve all imported keys
- for (int i = 0; i < result->imported && status; i++, status = status->next) {
- DEBUG(pakfire, "Imported key %s - %s\n",
- status->fpr, gpgme_strerror(status->result));
-
- // Fetch the key by its fingerprint
- r = pakfire_find_key(&key, pakfire, status->fpr);
- if (r)
- goto ERROR;
-
- // Append to array
- (*keys)[i] = key;
+ case PAKFIRE_KEY_EXPORT_MODE_PRIVATE:
+ r = pakfire_key_export_private_key(key, &private_key);
+ if (r)
+ goto ERROR;
- // Write key to keystore
- r = pakfire_key_write_to_keystore(key);
- if (r)
- goto ERROR;
- }
+ // Write the output
+ r = BIO_write(b64, &private_key, sizeof(private_key));
+ if (r < 0) {
+ ERROR(key->pakfire, "Could not write the private key\n");
+ r = 1;
+ goto ERROR;
}
break;
- // Input was invalid
- case GPG_ERR_INV_VALUE:
- errno = EINVAL;
- break;
-
- // Fall through for any other errors
default:
- ERROR(pakfire, "Failed with gpgme error: %s\n", gpgme_strerror(e));
- break;
+ errno = EINVAL;
+ r = 1;
+ goto ERROR;
}
- // Success
- r = 0;
+ // Flush all buffers
+ BIO_flush(b64);
-ERROR:
- if (result)
- gpgme_result_unref(result);
- gpgme_data_release(data);
+ // Append a trailing newline
+ fprintf(f, "\n");
- // Free keys on error
- if (r && keys && *keys) {
- for (struct pakfire_key** key = *keys; *key; key++)
- pakfire_key_unref(*key);
- free(*keys);
+ // Success!
+ r = 0;
- *keys = NULL;
- }
+ERROR:
+ if (b64)
+ BIO_free(b64);
+ if (bio)
+ BIO_free(bio);
return r;
}
PAKFIRE_EXPORT char* pakfire_key_dump(struct pakfire_key* key) {
- char date[1024];
- char* s = NULL;
+ char* buffer = NULL;
+ FILE* f = NULL;
+ int fd = -1;
int r;
- time_t created = pakfire_key_get_created(key);
-
- // Format creation time
- r = pakfire_strftime(date, "%Y-%m-%d", created);
- if (r)
+ // Allocate a buffer in memory
+ fd = memfd_create("pakfire-key-dump", MFD_CLOEXEC);
+ if (fd < 0) {
+ ERROR(key->pakfire, "Could not allocate a temporary file: %m\n");
+ r = 1;
goto ERROR;
+ }
- asprintf(&s, "pub %s%zu/%s %s",
- pakfire_key_get_pubkey_algo(key),
- pakfire_key_get_pubkey_length(key),
- pakfire_key_get_fingerprint(key),
- date
- );
+ // Re-open as FILE handle
+ f = fdopen(fd, "r+");
+ if (!f) {
+ ERROR(key->pakfire, "Could not open file handle for temporary file: %m\n");
+ r = 1;
+ goto ERROR;
+ }
- const char* uid = pakfire_key_get_uid(key);
- if (uid) {
- asprintf(&s, "%s\n %s", s, uid);
+ // Export the public part of the key
+ r = pakfire_key_export(key, f, PAKFIRE_KEY_EXPORT_MODE_PUBLIC);
+ if (r) {
+ ERROR(key->pakfire, "Could not export key: %m\n");
+ goto ERROR;
}
- time_t expires = pakfire_key_get_expires(key);
- if (expires) {
- r = pakfire_strftime(date, "%Y-%m-%d", expires);
- if (r)
- goto ERROR;
+ // Determine the length of the buffer
+ size_t length = ftell(f);
+
+ // Go back to the beginning of the file
+ rewind(f);
- asprintf(&s, "%s\n %s: %s", s, _("Expires"), date);
+ // Allocate a buffer
+ buffer = calloc(1, length + 1);
+ if (!buffer) {
+ ERROR(key->pakfire, "Could not allocate buffer of %zu byte(s)\n", length + 1);
+ r = 1;
+ goto ERROR;
}
- return s;
+ // Read everything into the buffer
+ size_t bytes_read = fread(buffer, 1, length, f);
+ if (bytes_read < length) {
+ ERROR(key->pakfire, "Could not read back the buffer: %m\n");
+ r = 1;
+ goto ERROR;
+ }
+
+ // Success!
+ r = 0;
ERROR:
- if (s)
- free(s);
+ if (f)
+ fclose(f);
+ if (fd >= 0)
+ close(fd);
+
+ // Free the buffer on error
+ if (r && buffer) {
+ free(buffer);
+ buffer = NULL;
+ }
- return NULL;
+ return buffer;
}
-int pakfire_key_sign(struct pakfire_key* key, const char* buffer, const size_t buffer_length,
- char** signature, size_t* signature_length, time_t* timestamp) {
- // Fetch GPGME context
- gpgme_ctx_t gpgctx = pakfire_get_gpgctx(key->pakfire);
- if (!gpgctx)
+/*
+ Sign
+*/
+
+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;
+
+ // Check inputs
+ if (!signature || !data || !length) {
+ errno = EINVAL;
return 1;
+ }
- // Remove any previous signers
- gpgme_signers_clear(gpgctx);
+ // Set algorithm
+ signature->sig_algo[0] = 'E';
+ signature->sig_algo[1] = 'd';
- gpgme_data_t data = NULL;
- gpgme_data_t sign = NULL;
- gpgme_error_t e;
- char* __signature = NULL;
- int r = 1;
+ // Set the key ID
+ memcpy(signature->key_id, key->id, sizeof(signature->key_id));
- // Enable the key
- e = gpgme_signers_add(gpgctx, key->gpgkey);
- if (gpgme_err_code(e)) {
- ERROR(key->pakfire, "Could not select key for signing: %s\n",
- gpgme_strerror(e));
+ // 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;
}
- // Initialize data buffer
- e = gpgme_data_new_from_mem(&data, buffer, buffer_length, 0);
- if (gpgme_err_code(e)) {
- ERROR(key->pakfire, "Could not initialize data buffer: %s\n",
- gpgme_strerror(e));
+ // Create a message digest context
+ mdctx = EVP_MD_CTX_new();
+ if (!mdctx) {
+ ERROR(key->pakfire, "Could not initialize the message digest context: %m\n");
+ r = 1;
goto ERROR;
}
- // Initialize signature buffer
- e = gpgme_data_new(&sign);
- if (gpgme_err_code(e)) {
- ERROR(key->pakfire, "Could not initialize signature buffer: %s\n",
- gpgme_strerror(e));
+ // Connect the two contexts
+ EVP_MD_CTX_set_pkey_ctx(mdctx, pctx);
+
+ size_t signature_length = sizeof(signature->signature);
+
+ // Create a signature over the digest
+ r = EVP_DigestSign(mdctx, signature->signature, &signature_length, data, length);
+ if (r < 1) {
+ ERR_error_string_n(ERR_get_error(), error, sizeof(error));
+
+ ERROR(key->pakfire, "Could not sign content: %s\n", error);
+ r = 1;
goto ERROR;
}
- // Create the signature
- e = gpgme_op_sign(gpgctx, data, sign, GPGME_SIG_MODE_DETACH);
- switch (gpgme_err_code(e)) {
- // Everything went OK
- case GPG_ERR_NO_ERROR:
- break;
+ // Success!
+ r = 0;
- default:
- ERROR(key->pakfire, "Could not sign: %s\n", gpgme_strerror(e));
+ERROR:
+ if (pctx)
+ EVP_PKEY_CTX_free(pctx);
+ if (mdctx)
+ EVP_MD_CTX_free(mdctx);
- // Set errno to something useful
- errno = gpgme_err_code_to_errno(e);
- goto ERROR;
- }
+ return r;
+}
- // Print some status details
- gpgme_sign_result_t result = gpgme_op_sign_result(gpgctx);
- if (result) {
- for (gpgme_new_signature_t s = result->signatures; s; s = s->next) {
- DEBUG(key->pakfire, "Signature created\n");
- DEBUG(key->pakfire, " Key : %s\n", s->fpr);
- DEBUG(key->pakfire, " Algorithm : %s\n", gpgme_pubkey_algo_name(s->pubkey_algo));
- DEBUG(key->pakfire, " Hash : %s\n", gpgme_hash_algo_name(s->hash_algo));
- DEBUG(key->pakfire, " Timestamp : %ld\n", s->timestamp);
+int pakfire_key_sign(struct pakfire_key* key,
+ FILE* f, const char* data, const size_t length, const char* comment) {
+ struct pakfire_key_signature signature = { 0 };
+ char* s = NULL;
+ int r;
+
+ // Create a signature
+ r = __pakfire_key_sign(key, &signature, data, length);
+ if (r)
+ return r;
- // Store timestamp
- if (timestamp)
- *timestamp = s->timestamp;
+ // Write the comment
+ if (comment) {
+ r = fprintf(f, "untrusted comment: %s\n", comment);
+ if (r < 0) {
+ ERROR(key->pakfire, "Could not write comment: %m\n");
+ r = 1;
+ goto ERROR;
}
}
- // Extract the signature
- __signature = gpgme_data_release_and_get_mem(sign, signature_length);
- if (!__signature) {
- ERROR(key->pakfire, "The signature was unexpectedly empty: %m\n");
+ // Encode the signature to base64
+ r = pakfire_b64encode(key->pakfire, &s, &signature, sizeof(signature));
+ if (r)
goto ERROR;
- }
- // Sign is now released and we should not do this again below
- sign = NULL;
-
- // Because GPGME could be using its internal allocator, we need to copy the signature
- // and release the memory that we got from GPGME using gpgme_free().
- *signature = malloc(*signature_length);
- if (!*signature)
+ // Write the signature
+ r = fprintf(f, "%s\n", s);
+ if (r < 0) {
+ ERROR(key->pakfire, "Could not write the signature: %m\n");
+ r = 1;
goto ERROR;
+ }
- memcpy(*signature, __signature, *signature_length);
-
- // Success
+ // Success!
r = 0;
ERROR:
- if (__signature)
- gpgme_free(__signature);
- if (data)
- gpgme_data_release(data);
- if (sign)
- gpgme_data_release(sign);
+ if (s)
+ free(s);
return r;
}
#include <pakfire/util.h>
#include "../testsuite.h"
-#include "key.h"
-static int test_init(const struct test* t) {
- struct pakfire_key** keys = NULL;
+static int test_generate(const struct test* t) {
+ struct pakfire_key* key = NULL;
int r = EXIT_FAILURE;
- // Try loading any keys & delete them all
- ASSERT_SUCCESS(pakfire_list_keys(t->pakfire, &keys));
- while (keys && *keys) {
- struct pakfire_key* key = *keys++;
+ // Try to call pakfire_key_generate() with some invalid inputs
+ ASSERT_ERRNO(pakfire_key_generate(&key, t->pakfire, PAKFIRE_KEY_ALGO_NULL), EINVAL);
- pakfire_key_delete(key);
- pakfire_key_unref(key);
- }
+ // Generate a new key using ed25519
+ ASSERT_SUCCESS(pakfire_key_generate(&key, t->pakfire, PAKFIRE_KEY_ALGO_ED25519));
- // Load list of keys again
- ASSERT_SUCCESS(pakfire_list_keys(t->pakfire, &keys));
+ // Write the public key to the console
+ ASSERT_SUCCESS(pakfire_key_export(key, stdout, PAKFIRE_KEY_EXPORT_MODE_PUBLIC));
- // Must be empty now
- ASSERT(keys == NULL);
+ // Write the private key to the console
+ ASSERT_SUCCESS(pakfire_key_export(key, stdout, PAKFIRE_KEY_EXPORT_MODE_PRIVATE));
// Everything passed
r = EXIT_SUCCESS;
FAIL:
+ if (key)
+ pakfire_key_unref(key);
+
return r;
}
-static int test_generate_key(const struct test* t, const char* algo, const char* userid) {
+static int test_sign(const struct test* t) {
struct pakfire_key* key = NULL;
- char* dump = NULL;
int r = EXIT_FAILURE;
- // Generate a new key
- ASSERT_SUCCESS(pakfire_key_generate(&key, t->pakfire, algo, userid));
+ const char* data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ const size_t length = strlen(data);
+
+ char* signature = NULL;
+ size_t signature_length = 0;
+
+ // Generate a new key using ed25519
+ ASSERT_SUCCESS(pakfire_key_generate(&key, t->pakfire, PAKFIRE_KEY_ALGO_ED25519));
- // Export the key
- ASSERT_SUCCESS(pakfire_key_export(key, stdout, PAKFIRE_KEY_EXPORT_MODE_SECRET));
+ // Write the public key to the console
+ ASSERT_SUCCESS(pakfire_key_export(key, stdout, PAKFIRE_KEY_EXPORT_MODE_PUBLIC));
- // Dump the key details
- ASSERT(dump = pakfire_key_dump(key));
- printf("%s\n", dump);
+ // 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"));
// Everything passed
r = EXIT_SUCCESS;
FAIL:
if (key)
pakfire_key_unref(key);
- if (dump)
- free(dump);
return r;
}
-static int test_generate(const struct test* t) {
+static int test_import_public(const struct test* t) {
struct pakfire_key* key = NULL;
+ FILE* f = NULL;
int r = EXIT_FAILURE;
- // Try to call pakfire_key_generate() with some invalid inputs
- ASSERT_ERRNO(pakfire_key_generate(&key, t->pakfire, NULL, NULL), EINVAL);
- ASSERT_ERRNO(pakfire_key_generate(&key, t->pakfire, "rsa2048", NULL), EINVAL);
- ASSERT_ERRNO(pakfire_key_generate(&key, t->pakfire, NULL, "Invalid Test Key"), EINVAL);
-
- // Generate a new key using RSA2048
- ASSERT_SUCCESS(test_generate_key(t, "rsa2048", "Test Key #1"));
+ // Open the public key
+ ASSERT(f = fopen(TEST_DATA_DIR "/keys/key1.pub", "r"));
- // Generate a new key using ed25519
- ASSERT_SUCCESS(test_generate_key(t, "ed25519", "Test Key #2"));
+ // Try to import the key
+ ASSERT_SUCCESS(pakfire_key_import(&key, t->pakfire, f));
- // Try to generate a key with the same ID
- ASSERT_ERRNO(pakfire_key_generate(&key, t->pakfire, "rsa2048", "Test Key #1"), EINVAL);
+ // Write the imported key to the console
+ ASSERT_SUCCESS(pakfire_key_export(key, stdout, PAKFIRE_KEY_EXPORT_MODE_PUBLIC));
// Everything passed
r = EXIT_SUCCESS;
FAIL:
if (key)
pakfire_key_unref(key);
+ if (f)
+ fclose(f);
return r;
}
-static int test_import_export(const struct test* t) {
- // Try to delete the key just in case it
- // has been imported before
- struct pakfire_key* key = pakfire_key_get(t->pakfire, TEST_KEY_FINGERPRINT);
- if (key) {
- pakfire_key_delete(key);
- pakfire_key_unref(key);
- }
-
-#if 0
- // Import a key
- struct pakfire_key** keys = pakfire_key_import(t->pakfire, TEST_KEY_DATA);
-
- // We should have a list with precisely one key object
- ASSERT(keys);
- ASSERT(keys[0] != NULL);
- ASSERT(keys[1] == NULL);
-
- // Get the imported key
- key = *keys;
+static int test_import_secret(const struct test* t) {
+ struct pakfire_key* key = NULL;
+ FILE* f = NULL;
+ int r = EXIT_FAILURE;
- // Check the fingerprint
- const char* fingerprint = pakfire_key_get_fingerprint(key);
- ASSERT(strcmp(fingerprint, TEST_KEY_FINGERPRINT) == 0);
+ // Open the public key
+ ASSERT(f = fopen(TEST_DATA_DIR "/keys/key1.sec", "r"));
- // Dump key description
- char* dump = pakfire_key_dump(key);
- ASSERT(dump);
- LOG("%s\n", dump);
- free(dump);
+ // Try to import the key
+ ASSERT_SUCCESS(pakfire_key_import(&key, t->pakfire, f));
- // Export the key
- char* data = pakfire_key_export(key, 0);
- ASSERT(data);
+ // Write the imported key to the console
+ ASSERT_SUCCESS(pakfire_key_export(key, stdout, PAKFIRE_KEY_EXPORT_MODE_PRIVATE));
- LOG("Exported key:\n%s\n", data);
- free(data);
+ // Everything passed
+ r = EXIT_SUCCESS;
- pakfire_key_unref(key);
-#endif
+FAIL:
+ if (key)
+ pakfire_key_unref(key);
+ if (f)
+ fclose(f);
- return EXIT_SUCCESS;
+ return r;
}
int main(int argc, const char* argv[]) {
- testsuite_add_test(test_init);
testsuite_add_test(test_generate);
- testsuite_add_test(test_import_export);
+ testsuite_add_test(test_sign);
+ testsuite_add_test(test_import_public);
+ testsuite_add_test(test_import_secret);
return testsuite_run(argc, argv);
}