Allow signing and verifying data using a PKCS-7 self-signed client certificate

This is a per-client certificate and private key that can be used to sign
metadata sent to remote servers, for instance success/failure reports.
This commit is contained in:
Richard Hughes 2019-03-07 16:01:02 +00:00
parent 26e1549d16
commit f28abe7fda
9 changed files with 619 additions and 16 deletions

View File

@ -175,6 +175,9 @@ if get_option('daemon')
endif
if get_option('pkcs7')
gnutls = dependency('gnutls', version : '>= 3.4.4.1')
if gnutls.version().version_compare('>= 3.6.0')
conf.set('HAVE_GNUTLS_3_6_0', '1')
endif
conf.set('ENABLE_PKCS7', '1')
endif
if get_option('gpg')

View File

@ -2171,7 +2171,8 @@ fu_engine_get_existing_keyring_result (FuEngine *self,
blob_sig = fu_common_get_contents_bytes (fwupd_remote_get_filename_cache_sig (remote), error);
if (blob_sig == NULL)
return NULL;
return fu_keyring_verify_data (kr, blob, blob_sig, error);
return fu_keyring_verify_data (kr, blob, blob_sig,
FU_KEYRING_VERIFY_FLAG_NONE, error);
}
/**
@ -2254,7 +2255,9 @@ fu_engine_update_metadata (FuEngine *self, const gchar *remote_id,
pki_dir = g_build_filename (sysconfdir, "pki", "fwupd-metadata", NULL);
if (!fu_keyring_add_public_keys (kr, pki_dir, error))
return FALSE;
kr_result = fu_keyring_verify_data (kr, bytes_raw, bytes_sig, error);
kr_result = fu_keyring_verify_data (kr, bytes_raw, bytes_sig,
FU_KEYRING_VERIFY_FLAG_NONE,
error);
if (kr_result == NULL)
return FALSE;

View File

@ -231,6 +231,7 @@ static FuKeyringResult *
fu_keyring_gpg_verify_data (FuKeyring *keyring,
GBytes *blob,
GBytes *blob_signature,
FuKeyringVerifyFlags flags,
GError **error)
{
FuKeyringGpg *self = FU_KEYRING_GPG (keyring);
@ -242,6 +243,15 @@ fu_keyring_gpg_verify_data (FuKeyring *keyring,
g_auto(gpgme_data_t) sig = NULL;
g_autoptr(GString) authority_newest = g_string_new (NULL);
/* not supported */
if (flags & FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"no GPG client certificate support");
return NULL;
}
/* load file data */
rc = gpgme_data_new_from_mem (&data,
g_bytes_get_data (blob, NULL),

View File

@ -8,8 +8,11 @@
#include "config.h"
#include <gnutls/abstract.h>
#include <gnutls/crypto.h>
#include <gnutls/pkcs7.h>
#include "fu-common.h"
#include "fu-keyring-pkcs7.h"
#include "fwupd-error.h"
@ -22,11 +25,20 @@ struct _FuKeyringPkcs7
G_DEFINE_TYPE (FuKeyringPkcs7, fu_keyring_pkcs7, FU_TYPE_KEYRING)
typedef guchar gnutls_data_t;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_pkcs7_t, gnutls_pkcs7_deinit, NULL)
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_privkey_t, gnutls_privkey_deinit, NULL)
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_pubkey_t, gnutls_pubkey_deinit, NULL)
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_x509_crt_t, gnutls_x509_crt_deinit, NULL)
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_x509_dn_t, gnutls_x509_dn_deinit, NULL)
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_x509_privkey_t, gnutls_x509_privkey_deinit, NULL)
#ifdef HAVE_GNUTLS_3_6_0
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_x509_spki_t, gnutls_x509_spki_deinit, NULL)
#endif
G_DEFINE_AUTOPTR_CLEANUP_FUNC(gnutls_data_t, gnutls_free)
#pragma clang diagnostic pop
static gnutls_x509_crt_t
@ -151,6 +163,362 @@ fu_keyring_pkcs7_add_public_keys (FuKeyring *keyring,
return TRUE;
}
static gnutls_privkey_t
fu_keyring_pkcs7_load_privkey (FuKeyringPkcs7 *self, GError **error)
{
int rc;
gnutls_datum_t d = { 0 };
gsize bufsz = 0;
g_autofree gchar *buf = NULL;
g_autofree gchar *fn = NULL;
g_autofree gchar *localstatedir = NULL;
g_auto(gnutls_privkey_t) key = NULL;
/* load the private key */
rc = gnutls_privkey_init (&key);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"privkey_init: %s [%i]",
gnutls_strerror (rc), rc);
return NULL;
}
localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
fn = g_build_filename (localstatedir, "pki", "secret.key", NULL);
if (!g_file_get_contents (fn, &buf, &bufsz, error))
return NULL;
d.size = bufsz;
d.data = (unsigned char *) buf;
rc = gnutls_privkey_import_x509_raw (key, &d, GNUTLS_X509_FMT_PEM, NULL, 0);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"privkey_import_x509_raw: %s [%i]",
gnutls_strerror (rc), rc);
return NULL;
}
return g_steal_pointer (&key);
}
static gnutls_x509_crt_t
fu_keyring_pkcs7_load_client_certificate (FuKeyringPkcs7 *self, GError **error)
{
g_autofree gchar *filename = NULL;
g_autofree gchar *localstatedir = NULL;
localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
filename = g_build_filename (localstatedir, "pki", "client.pem", NULL);
return fu_keyring_pkcs7_load_crt_from_filename (filename,
GNUTLS_X509_FMT_PEM,
error);
}
static gnutls_pubkey_t
fu_keyring_pkcs7_load_pubkey_from_privkey (gnutls_privkey_t privkey, GError **error)
{
g_auto(gnutls_pubkey_t) pubkey = NULL;
int rc;
/* get the public key part of the private key */
rc = gnutls_pubkey_init (&pubkey);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"pubkey_init: %s [%i]",
gnutls_strerror (rc), rc);
return NULL;
}
rc = gnutls_pubkey_import_privkey (pubkey, privkey, 0, 0);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"pubkey_import_privkey: %s [%i]",
gnutls_strerror (rc), rc);
return NULL;
}
/* success */
return g_steal_pointer (&pubkey);
}
/* generates a private key just like:
* `certtool --generate-privkey` */
static gboolean
fu_keyring_pkcs7_ensure_private_key (FuKeyringPkcs7 *self, GError **error)
{
#ifdef HAVE_GNUTLS_3_6_0
gnutls_datum_t d = { 0 };
int bits;
int key_type = GNUTLS_PK_RSA;
int rc;
g_autofree gchar *fn = NULL;
g_autofree gchar *localstatedir = NULL;
g_auto(gnutls_x509_privkey_t) key = NULL;
g_auto(gnutls_x509_spki_t) spki = NULL;
g_autoptr(GFile) file = NULL;
g_autoptr(gnutls_data_t) d_payload = NULL;
/* check exists */
localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
fn = g_build_filename (localstatedir, "pki", "secret.key", NULL);
if (g_file_test (fn, G_FILE_TEST_EXISTS))
return TRUE;
/* initialize key and SPKI */
rc = gnutls_x509_privkey_init (&key);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"privkey_init: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
rc = gnutls_x509_spki_init (&spki);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"spki_init: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
/* generate key */
bits = gnutls_sec_param_to_pk_bits (key_type, GNUTLS_SEC_PARAM_HIGH);
g_debug ("generating a %d bit %s private key...",
bits, gnutls_pk_algorithm_get_name (key_type));
rc = gnutls_x509_privkey_generate2(key, key_type, bits, 0, NULL, 0);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"privkey_generate2: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
rc = gnutls_x509_privkey_verify_params (key);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"privkey_verify_params: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
/* create parents if required */
if (!fu_common_mkdir_parent (fn, error))
return FALSE;
/* save to file */
rc = gnutls_x509_privkey_export2 (key, GNUTLS_X509_FMT_PEM, &d);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"privkey_export2: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
d_payload = d.data;
file = g_file_new_for_path (fn);
return g_file_replace_contents (file, (const char *) d_payload, d.size,
NULL, FALSE, G_FILE_CREATE_PRIVATE, NULL,
NULL, error);
#else
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"cannot build private key as GnuTLS version is too old");
return FALSE;
#endif
}
/* generates a self signed certificate just like:
* `certtool --generate-self-signed --load-privkey priv.pem` */
static gboolean
fu_keyring_pkcs7_ensure_client_certificate (FuKeyringPkcs7 *self, GError **error)
{
int rc;
gnutls_datum_t d = { 0 };
guchar sha1buf[20];
gsize sha1bufsz = sizeof(sha1buf);
g_autofree gchar *fn = NULL;
g_autofree gchar *localstatedir = NULL;
g_auto(gnutls_privkey_t) key = NULL;
g_auto(gnutls_pubkey_t) pubkey = NULL;
g_auto(gnutls_x509_crt_t) crt = NULL;
g_autoptr(gnutls_data_t) d_payload = NULL;
/* check exists */
localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
fn = g_build_filename (localstatedir, "pki", "client.pem", NULL);
if (g_file_test (fn, G_FILE_TEST_EXISTS))
return TRUE;
/* ensure the private key exists */
if (!fu_keyring_pkcs7_ensure_private_key (self, error)) {
g_prefix_error (error, "failed to generate private key: ");
return FALSE;
}
/* load private key */
key = fu_keyring_pkcs7_load_privkey (self, error);
if (key == NULL)
return FALSE;
/* load the public key from the private key */
pubkey = fu_keyring_pkcs7_load_pubkey_from_privkey (key, error);
if (pubkey == NULL)
return FALSE;
/* create certificate */
rc = gnutls_x509_crt_init (&crt);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"crt_init: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
/* set public key */
rc = gnutls_x509_crt_set_pubkey (crt, pubkey);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"crt_set_pubkey: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
/* set positive random serial number */
rc = gnutls_rnd (GNUTLS_RND_NONCE, sha1buf, sizeof(sha1buf));
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"gnutls_rnd: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
sha1buf[0] &= 0x7f;
rc = gnutls_x509_crt_set_serial(crt, sha1buf, sizeof(sha1buf));
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"crt_set_serial: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
/* set activation */
rc = gnutls_x509_crt_set_activation_time (crt, time (NULL));
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"set_activation_time: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
/* set expiration */
rc = gnutls_x509_crt_set_expiration_time (crt, (time_t) -1);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"set_expiration_time: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
/* set basic constraints */
rc = gnutls_x509_crt_set_basic_constraints (crt, 0, -1);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"set_basic_constraints: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
/* set usage */
rc = gnutls_x509_crt_set_key_usage (crt, GNUTLS_KEY_DIGITAL_SIGNATURE);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"set_key_usage: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
/* set subject key ID */
rc = gnutls_x509_crt_get_key_id (crt, GNUTLS_KEYID_USE_SHA1, sha1buf, &sha1bufsz);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"get_key_id: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
rc = gnutls_x509_crt_set_subject_key_id (crt, sha1buf, sha1bufsz);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"set_subject_key_id: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
/* set version */
rc = gnutls_x509_crt_set_version (crt, 3);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"error setting certificate version: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
/* self-sign certificate */
rc = gnutls_x509_crt_privkey_sign (crt, crt, key, GNUTLS_DIG_SHA256, 0);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"crt_privkey_sign: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
/* export to file */
rc = gnutls_x509_crt_export2 (crt, GNUTLS_X509_FMT_PEM, &d);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"crt_export2: %s [%i]",
gnutls_strerror (rc), rc);
return FALSE;
}
d_payload = d.data;
return g_file_set_contents (fn, (const gchar *) d_payload, d.size, error);
}
static gboolean
fu_keyring_pkcs7_setup (FuKeyring *keyring, GError **error)
{
@ -205,10 +573,13 @@ fu_keyring_pkcs7_datum_to_dn_str (const gnutls_datum_t *raw)
return g_strndup ((const gchar *) str->data, str->size);
}
/* verifies a detached signature just like:
* `certtool --p7-verify --load-certificate client.pem --infile=test.p7b` */
static FuKeyringResult *
fu_keyring_pkcs7_verify_data (FuKeyring *keyring,
GBytes *blob,
GBytes *blob_signature,
FuKeyringVerifyFlags flags,
GError **error)
{
FuKeyringPkcs7 *self = FU_KEYRING_PKCS7 (keyring);
@ -217,8 +588,15 @@ fu_keyring_pkcs7_verify_data (FuKeyring *keyring,
int count;
int rc;
g_auto(gnutls_pkcs7_t) pkcs7 = NULL;
g_auto(gnutls_x509_crt_t) crt = NULL;
g_autoptr(GString) authority_newest = g_string_new (NULL);
/* ensure the client certificate exists */
if (!fu_keyring_pkcs7_ensure_client_certificate (self, error)) {
g_prefix_error (error, "failed to generate client certificate: ");
return NULL;
}
/* startup */
rc = gnutls_pkcs7_init (&pkcs7);
if (rc != GNUTLS_E_SUCCESS) {
@ -255,17 +633,33 @@ fu_keyring_pkcs7_verify_data (FuKeyring *keyring,
"no PKCS7 signatures found");
return NULL;
}
/* use client certificate */
if (flags & FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT) {
if (!fu_keyring_pkcs7_ensure_client_certificate (self, error)) {
g_prefix_error (error, "failed to generate client certificate: ");
return NULL;
}
crt = fu_keyring_pkcs7_load_client_certificate (self, error);
if (crt == NULL)
return NULL;
}
for (gint i = 0; i < count; i++) {
gnutls_pkcs7_signature_info_st info;
gint64 signing_time = 0;
/* verify the data against the detached signature */
rc = gnutls_pkcs7_verify (pkcs7, self->tl,
NULL, /* vdata */
0, /* vdata_size */
i, /* index */
&datum, /* data */
0); /* flags */
if (flags & FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT) {
rc = gnutls_pkcs7_verify_direct (pkcs7, crt, i, &datum, 0);
} else {
rc = gnutls_pkcs7_verify (pkcs7, self->tl,
NULL, /* vdata */
0, /* vdata_size */
i, /* index */
&datum, /* data */
0); /* flags */
}
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
@ -303,6 +697,107 @@ fu_keyring_pkcs7_verify_data (FuKeyring *keyring,
NULL));
}
/* creates a detached signature just like:
* `certtool --p7-detached-sign --load-certificate client.pem \
* --load-privkey secret.pem --outfile=test.p7b` */
static GBytes *
fu_keyring_pkcs7_sign_data (FuKeyring *keyring,
GBytes *blob,
FuKeyringSignFlags flags,
GError **error)
{
FuKeyringPkcs7 *self = FU_KEYRING_PKCS7 (keyring);
gnutls_datum_t d = { 0 };
gnutls_digest_algorithm_t dig = GNUTLS_DIG_NULL;
guint gnutls_flags = 0;
int rc;
g_auto(gnutls_pkcs7_t) pkcs7 = NULL;
g_auto(gnutls_privkey_t) key = NULL;
g_auto(gnutls_pubkey_t) pubkey = NULL;
g_auto(gnutls_x509_crt_t) crt = NULL;
g_autoptr(gnutls_data_t) d_payload = NULL;
/* ensure the client certificate exists */
if (!fu_keyring_pkcs7_ensure_client_certificate (self, error)) {
g_prefix_error (error, "failed to generate client certificate: ");
return NULL;
}
/* import the keys */
crt = fu_keyring_pkcs7_load_client_certificate (self, error);
if (crt == NULL)
return NULL;
key = fu_keyring_pkcs7_load_privkey (self, error);
if (key == NULL)
return NULL;
/* get the digest algorithm from the publix key */
pubkey = fu_keyring_pkcs7_load_pubkey_from_privkey (key, error);
if (pubkey == NULL)
return NULL;
rc = gnutls_pubkey_get_preferred_hash_algorithm (pubkey, &dig, NULL);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"preferred_hash_algorithm: %s [%i]",
gnutls_strerror (rc), rc);
return NULL;
}
/* create container */
rc = gnutls_pkcs7_init (&pkcs7);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"pkcs7_init: %s [%i]",
gnutls_strerror (rc), rc);
return NULL;
}
/* sign data */
d.data = (unsigned char *) g_bytes_get_data (blob, NULL);
d.size = g_bytes_get_size (blob);
if (flags & FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP)
gnutls_flags |= GNUTLS_PKCS7_INCLUDE_TIME;
if (flags & FU_KEYRING_SIGN_FLAG_ADD_CERT)
gnutls_flags |= GNUTLS_PKCS7_INCLUDE_CERT;
rc = gnutls_pkcs7_sign (pkcs7, crt, key, &d, NULL, NULL, dig, gnutls_flags);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"pkcs7_sign: %s [%i]",
gnutls_strerror (rc), rc);
return NULL;
}
/* set certificate */
if (flags & FU_KEYRING_SIGN_FLAG_ADD_CERT) {
rc = gnutls_pkcs7_set_crt (pkcs7, crt);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"pkcs7_set_cr: %s", gnutls_strerror (rc));
return NULL;
}
}
/* export */
rc = gnutls_pkcs7_export2 (pkcs7, GNUTLS_X509_FMT_PEM, &d);
if (rc < 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_SIGNATURE_INVALID,
"pkcs7_export: %s", gnutls_strerror (rc));
return NULL;
}
d_payload = d.data;
return g_bytes_new (d_payload, d.size);
}
static void
fu_keyring_pkcs7_finalize (GObject *object)
{
@ -318,6 +813,7 @@ fu_keyring_pkcs7_class_init (FuKeyringPkcs7Class *klass)
FuKeyringClass *klass_app = FU_KEYRING_CLASS (klass);
klass_app->setup = fu_keyring_pkcs7_setup;
klass_app->add_public_keys = fu_keyring_pkcs7_add_public_keys;
klass_app->sign_data = fu_keyring_pkcs7_sign_data;
klass_app->verify_data = fu_keyring_pkcs7_verify_data;
object_class->finalize = fu_keyring_pkcs7_finalize;
}

View File

@ -159,7 +159,9 @@ fu_keyring_get_release_flags (XbNode *release,
fu_keyring_get_name (kr));
return FALSE;
}
kr_result = fu_keyring_verify_data (kr, blob_payload, blob_signature, &error_local);
kr_result = fu_keyring_verify_data (kr, blob_payload, blob_signature,
FU_KEYRING_VERIFY_FLAG_NONE,
&error_local);
if (kr_result == NULL) {
g_warning ("untrusted as failed to verify from %s keyring: %s",
fu_keyring_get_name (kr),

View File

@ -40,13 +40,33 @@ FuKeyringResult *
fu_keyring_verify_data (FuKeyring *keyring,
GBytes *blob,
GBytes *blob_signature,
FuKeyringVerifyFlags flags,
GError **error)
{
FuKeyringClass *klass = FU_KEYRING_GET_CLASS (keyring);
g_return_val_if_fail (FU_IS_KEYRING (keyring), NULL);
g_return_val_if_fail (blob != NULL, NULL);
g_return_val_if_fail (blob_signature != NULL, NULL);
return klass->verify_data (keyring, blob, blob_signature, error);
return klass->verify_data (keyring, blob, blob_signature, flags, error);
}
GBytes *
fu_keyring_sign_data (FuKeyring *keyring,
GBytes *blob,
FuKeyringSignFlags flags,
GError **error)
{
FuKeyringClass *klass = FU_KEYRING_GET_CLASS (keyring);
g_return_val_if_fail (FU_IS_KEYRING (keyring), NULL);
g_return_val_if_fail (blob != NULL, NULL);
if (klass->sign_data == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"signing data is not supported");
return NULL;
}
return klass->sign_data (keyring, blob, flags, error);
}
const gchar *

View File

@ -16,6 +16,19 @@ G_BEGIN_DECLS
#define FU_TYPE_KEYRING (fu_keyring_get_type ())
G_DECLARE_DERIVABLE_TYPE (FuKeyring, fu_keyring, FU, KEYRING, GObject)
typedef enum {
FU_KEYRING_VERIFY_FLAG_NONE = 0,
FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT = 1 << 1,
FU_KEYRING_VERIFY_FLAG_LAST
} FuKeyringVerifyFlags;
typedef enum {
FU_KEYRING_SIGN_FLAG_NONE = 0,
FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP = 1 << 0,
FU_KEYRING_SIGN_FLAG_ADD_CERT = 1 << 1,
FU_KEYRING_SIGN_FLAG_LAST
} FuKeyringSignFlags;
struct _FuKeyringClass
{
GObjectClass parent_class;
@ -27,6 +40,11 @@ struct _FuKeyringClass
FuKeyringResult *(*verify_data) (FuKeyring *keyring,
GBytes *payload,
GBytes *payload_signature,
FuKeyringVerifyFlags flags,
GError **error);
GBytes *(*sign_data) (FuKeyring *keyring,
GBytes *payload,
FuKeyringSignFlags flags,
GError **error);
};
@ -38,6 +56,11 @@ gboolean fu_keyring_add_public_keys (FuKeyring *keyring,
FuKeyringResult *fu_keyring_verify_data (FuKeyring *keyring,
GBytes *blob,
GBytes *blob_signature,
FuKeyringVerifyFlags flags,
GError **error);
GBytes *fu_keyring_sign_data (FuKeyring *keyring,
GBytes *blob,
FuKeyringSignFlags flags,
GError **error);
const gchar *fu_keyring_get_name (FuKeyring *self);
void fu_keyring_set_name (FuKeyring *self,

View File

@ -2400,7 +2400,9 @@ fu_keyring_gpg_func (void)
g_assert_no_error (error);
g_assert_nonnull (blob_pass);
blob_sig = g_bytes_new_static (sig_gpgme, strlen (sig_gpgme));
result_pass = fu_keyring_verify_data (keyring, blob_pass, blob_sig, &error);
result_pass = fu_keyring_verify_data (keyring, blob_pass, blob_sig,
FU_KEYRING_VERIFY_FLAG_NONE,
&error);
g_assert_no_error (error);
g_assert_nonnull (result_pass);
g_assert_cmpint (fu_keyring_result_get_timestamp (result_pass), == , 1438072952);
@ -2413,7 +2415,8 @@ fu_keyring_gpg_func (void)
blob_fail = fu_common_get_contents_bytes (fw_fail, &error);
g_assert_no_error (error);
g_assert_nonnull (blob_fail);
result_fail = fu_keyring_verify_data (keyring, blob_fail, blob_sig, &error);
result_fail = fu_keyring_verify_data (keyring, blob_fail, blob_sig,
FU_KEYRING_VERIFY_FLAG_NONE, &error);
g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_SIGNATURE_INVALID);
g_assert_null (result_fail);
g_clear_error (&error);
@ -2463,7 +2466,8 @@ fu_keyring_pkcs7_func (void)
blob_sig = fu_common_get_contents_bytes (sig_fn, &error);
g_assert_no_error (error);
g_assert_nonnull (blob_sig);
result_pass = fu_keyring_verify_data (keyring, blob_pass, blob_sig, &error);
result_pass = fu_keyring_verify_data (keyring, blob_pass, blob_sig,
FU_KEYRING_VERIFY_FLAG_NONE, &error);
g_assert_no_error (error);
g_assert_nonnull (result_pass);
g_assert_cmpint (fu_keyring_result_get_timestamp (result_pass), >= , 1502871248);
@ -2475,7 +2479,8 @@ fu_keyring_pkcs7_func (void)
blob_sig2 = fu_common_get_contents_bytes (sig_fn2, &error);
g_assert_no_error (error);
g_assert_nonnull (blob_sig2);
result_fail = fu_keyring_verify_data (keyring, blob_pass, blob_sig2, &error);
result_fail = fu_keyring_verify_data (keyring, blob_pass, blob_sig2,
FU_KEYRING_VERIFY_FLAG_NONE, &error);
g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_SIGNATURE_INVALID);
g_assert_null (result_fail);
g_clear_error (&error);
@ -2486,7 +2491,8 @@ fu_keyring_pkcs7_func (void)
blob_fail = fu_common_get_contents_bytes (fw_fail, &error);
g_assert_no_error (error);
g_assert_nonnull (blob_fail);
result_fail = fu_keyring_verify_data (keyring, blob_fail, blob_sig, &error);
result_fail = fu_keyring_verify_data (keyring, blob_fail, blob_sig,
FU_KEYRING_VERIFY_FLAG_NONE, &error);
g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_SIGNATURE_INVALID);
g_assert_null (result_fail);
g_clear_error (&error);
@ -2495,6 +2501,45 @@ fu_keyring_pkcs7_func (void)
#endif
}
static void
fu_keyring_pkcs7_self_signed_func (void)
{
#ifdef ENABLE_PKCS7
gboolean ret;
g_autoptr(FuKeyring) kr = NULL;
g_autoptr(FuKeyringResult) kr_result = NULL;
g_autoptr(GBytes) payload = NULL;
g_autoptr(GBytes) signature = NULL;
g_autoptr(GError) error = NULL;
#ifndef HAVE_GNUTLS_3_6_0
/* required to create the private key correctly */
g_test_skip ("GnuTLS version too old");
#endif
/* create detached signature and verify */
kr = fu_keyring_pkcs7_new ();
ret = fu_keyring_setup (kr, &error);
g_assert_no_error (error);
g_assert_true (ret);
payload = fu_common_get_contents_bytes ("/etc/machine-id", &error);
g_assert_no_error (error);
g_assert_nonnull (payload);
signature = fu_keyring_sign_data (kr, payload, FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP, &error);
g_assert_no_error (error);
g_assert_nonnull (signature);
ret = fu_common_set_contents_bytes ("/tmp/test.p7b", signature, &error);
g_assert_no_error (error);
g_assert_true (ret);
kr_result = fu_keyring_verify_data (kr, payload, signature,
FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT, &error);
g_assert_no_error (error);
g_assert_nonnull (kr_result);
#else
g_test_skip ("no GnuTLS support enabled");
#endif
}
static void
fu_common_firmware_builder_func (void)
{
@ -3523,6 +3568,7 @@ main (int argc, char **argv)
g_test_add_func ("/fwupd/plugin{composite}", fu_plugin_composite_func);
g_test_add_func ("/fwupd/keyring{gpg}", fu_keyring_gpg_func);
g_test_add_func ("/fwupd/keyring{pkcs7}", fu_keyring_pkcs7_func);
g_test_add_func ("/fwupd/keyring{pkcs7-self-signed}", fu_keyring_pkcs7_self_signed_func);
g_test_add_func ("/fwupd/plugin{build-hash}", fu_plugin_hash_func);
g_test_add_func ("/fwupd/chunk", fu_chunk_func);
g_test_add_func ("/fwupd/common{version-guess-format}", fu_common_version_guess_format_func);

View File

@ -398,7 +398,7 @@ if get_option('tests')
'-DFU_MUTEX_DEBUG',
],
)
test('fu-self-test', e, is_parallel:false)
test('fu-self-test', e, is_parallel:false, timeout:180)
endif
if get_option('introspection')