#include <linux/fsverity.h>
#endif
+#if defined(HAVE_OPENSSL)
+#include <openssl/bio.h>
+#include <openssl/engine.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs7.h>
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (X509, X509_free);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (EVP_PKEY, EVP_PKEY_free);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (BIO, BIO_free);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (PKCS7, PKCS7_free);
+#endif
+
gboolean
_ostree_repo_parse_fsverity_config (OstreeRepo *self, GError **error)
{
#endif
return TRUE;
}
+
+#if defined(HAVE_OPENSSL)
+static gboolean
+read_pem_x509_certificate (const char *certfile, X509 **cert_ret, GError **error)
+{
+ g_autoptr (BIO) bio = NULL;
+ X509 *cert;
+
+ errno = 0;
+ bio = BIO_new_file (certfile, "r");
+ if (!bio)
+ return glnx_throw_errno_prefix (error, "Error loading composefs certfile '%s'", certfile);
+
+ cert = PEM_read_bio_X509 (bio, NULL, NULL, NULL);
+ if (!cert)
+ return glnx_throw (error, "Error parsing composefs certfile '%s'", certfile);
+
+ *cert_ret = cert;
+ return TRUE;
+}
+
+static gboolean
+read_pem_pkcs8_private_key (const char *keyfile, EVP_PKEY **pkey_ret, GError **error)
+{
+ g_autoptr (BIO) bio;
+ EVP_PKEY *pkey;
+
+ errno = 0;
+ bio = BIO_new_file (keyfile, "r");
+ if (!bio)
+ return glnx_throw_errno_prefix (error, "Error loading composefs keyfile '%s'", keyfile);
+
+ pkey = PEM_read_bio_PrivateKey (bio, NULL, NULL, NULL);
+ if (!pkey)
+ return glnx_throw (error, "Error parsing composefs keyfile '%s'", keyfile);
+
+ *pkey_ret = pkey;
+ return TRUE;
+}
+
+static gboolean
+sign_pkcs7 (const void *data_to_sign, size_t data_size, EVP_PKEY *pkey, X509 *cert,
+ const EVP_MD *md, BIO **res, GError **error)
+{
+ int pkcs7_flags = PKCS7_BINARY | PKCS7_DETACHED | PKCS7_NOATTR | PKCS7_NOCERTS | PKCS7_PARTIAL;
+ g_autoptr (BIO) bio = NULL;
+ g_autoptr (BIO) bio_res = NULL;
+ g_autoptr (PKCS7) p7 = NULL;
+
+ bio = BIO_new_mem_buf ((void *)data_to_sign, data_size);
+ if (!bio)
+ return glnx_throw (error, "Can't allocate buffer");
+
+ p7 = PKCS7_sign (NULL, NULL, NULL, bio, pkcs7_flags);
+ if (!p7)
+ return glnx_throw (error, "Can't initialize PKCS#7");
+
+ if (!PKCS7_sign_add_signer (p7, cert, pkey, md, pkcs7_flags))
+ return glnx_throw (error, "Can't add signer to PKCS#7");
+
+ if (PKCS7_final (p7, bio, pkcs7_flags) != 1)
+ return glnx_throw (error, "Can't finalize PKCS#7");
+
+ bio_res = BIO_new (BIO_s_mem ());
+ if (!bio_res)
+ return glnx_throw (error, "Can't allocate buffer");
+
+ if (i2d_PKCS7_bio (bio_res, p7) != 1)
+ return glnx_throw (error, "Can't DER-encode PKCS#7 signature object");
+
+ *res = g_steal_pointer (&bio_res);
+ return TRUE;
+}
+
+gboolean
+_ostree_fsverity_sign (const char *certfile, const char *keyfile, const guchar *fsverity_digest,
+ GBytes **data_out, GCancellable *cancellable, GError **error)
+{
+ g_autofree struct fsverity_formatted_digest *d = NULL;
+ gsize d_size;
+ g_autoptr (X509) cert = NULL;
+ g_autoptr (EVP_PKEY) pkey = NULL;
+ g_autoptr (BIO) bio_sig = NULL;
+ const EVP_MD *md;
+ guchar *sig;
+ long sig_size;
+
+ if (certfile == NULL)
+ return glnx_throw (error, "certfile not specified");
+
+ if (keyfile == NULL)
+ return glnx_throw (error, "keyfile not specified");
+
+ if (!read_pem_x509_certificate (certfile, &cert, error))
+ return FALSE;
+
+ if (!read_pem_pkcs8_private_key (keyfile, &pkey, error))
+ return FALSE;
+
+ md = EVP_sha256 ();
+ if (md == NULL)
+ return glnx_throw (error, "No sha256 support in openssl");
+
+ d_size = sizeof (struct fsverity_formatted_digest) + OSTREE_SHA256_DIGEST_LEN;
+ d = g_malloc0 (d_size);
+
+ memcpy (d->magic, "FSVerity", 8);
+ d->digest_algorithm = GUINT16_TO_LE (FS_VERITY_HASH_ALG_SHA256);
+ d->digest_size = GUINT16_TO_LE (OSTREE_SHA256_DIGEST_LEN);
+ memcpy (d->digest, fsverity_digest, OSTREE_SHA256_DIGEST_LEN);
+
+ if (!sign_pkcs7 (d, d_size, pkey, cert, md, &bio_sig, error))
+ return FALSE;
+
+ sig_size = BIO_get_mem_data (bio_sig, &sig);
+
+ *data_out = g_bytes_new (sig, sig_size);
+
+ return TRUE;
+}
+#else
+gboolean
+_ostree_fsverity_sign (const char *certfile, const char *keyfile, const guchar *fsverity_digest,
+ GBytes **data_out, GCancellable *cancellable, GError **error)
+{
+ return glnx_throw (error, "fsverity signature support not built");
+}
+#endif