aboutsummaryrefslogtreecommitdiff
path: root/lib/libsecureboot/openpgp/opgp_key.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libsecureboot/openpgp/opgp_key.c')
-rw-r--r--lib/libsecureboot/openpgp/opgp_key.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/lib/libsecureboot/openpgp/opgp_key.c b/lib/libsecureboot/openpgp/opgp_key.c
new file mode 100644
index 000000000000..568d334ec117
--- /dev/null
+++ b/lib/libsecureboot/openpgp/opgp_key.c
@@ -0,0 +1,413 @@
+/*-
+ * Copyright (c) 2018, Juniper Networks, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include "../libsecureboot-priv.h"
+
+#include "decode.h"
+#include "packet.h"
+
+/**
+ * @brief decode user-id packet
+ *
+ * This is trivial
+ *
+ * @sa rfc4880:5.11
+ */
+ssize_t
+decode_user(int tag, unsigned char **pptr, size_t len, OpenPGP_user *user)
+{
+ char *cp;
+
+ if (tag == 13) {
+ user->id = malloc(len + 1);
+ strncpy(user->id, (char *)*pptr, len);
+ user->id[len] = '\0';
+ user->name = user->id;
+ cp = strchr(user->id, '<');
+ if (cp > user->id) {
+ user->id = strdup(user->id);
+ cp[-1] = '\0';
+ }
+ }
+ *pptr += len;
+ return ((ssize_t)len);
+}
+
+/**
+ * @brief decode a key packet
+ *
+ * We only really support v4 and RSA
+ *
+ * @sa rfc4880:5.5.1.1
+ */
+ssize_t
+decode_key(int tag, unsigned char **pptr, size_t len, OpenPGP_key *key)
+{
+ unsigned char *ptr;
+ int version;
+#ifdef USE_BEARSSL
+ br_sha1_context mctx;
+ unsigned char mdata[br_sha512_SIZE];
+ size_t mlen;
+#else
+ RSA *rsa = NULL;
+ const EVP_MD *md = NULL;
+ EVP_MD_CTX mctx;
+ unsigned char mdata[EVP_MAX_MD_SIZE];
+ unsigned int mlen;
+#endif
+
+ if (tag != 6)
+ return (-1);
+
+ key->key = NULL;
+ ptr = *pptr;
+ version = *ptr;
+ if (version == 4) { /* all we support really */
+ /* comput key fingerprint and id @sa rfc4880:12.2 */
+ mdata[0] = 0x99; /* rfc4880: 12.2.a.1 */
+ mdata[1] = (len >> 8) & 0xff;
+ mdata[2] = len & 0xff;
+
+#ifdef USE_BEARSSL
+ br_sha1_init(&mctx);
+ br_sha1_update(&mctx, mdata, 3);
+ br_sha1_update(&mctx, ptr, len);
+ br_sha1_out(&mctx, mdata);
+ mlen = br_sha1_SIZE;
+#else
+ md = EVP_get_digestbyname("sha1");
+ EVP_DigestInit(&mctx, md);
+ EVP_DigestUpdate(&mctx, mdata, 3);
+ EVP_DigestUpdate(&mctx, ptr, len);
+ mlen = (unsigned int)sizeof(mdata);
+ EVP_DigestFinal(&mctx, mdata, &mlen);
+#endif
+ key->id = octets2hex(&mdata[mlen - 8], 8);
+ }
+ ptr += 1; /* done with version */
+ ptr += 4; /* skip ctime */
+ if (version == 3)
+ ptr += 2; /* valid days */
+ key->sig_alg = *ptr++;
+ if (key->sig_alg == 1) { /* RSA */
+#ifdef USE_BEARSSL
+ key->key = NEW(br_rsa_public_key);
+ if (!key->key)
+ goto oops;
+ key->key->n = mpi2bn(&ptr, &key->key->nlen);
+ key->key->e = mpi2bn(&ptr, &key->key->elen);
+#else
+ rsa = RSA_new();
+ if (!rsa)
+ goto oops;
+ rsa->n = mpi2bn(&ptr);
+ rsa->e = mpi2bn(&ptr);
+ key->key = EVP_PKEY_new();
+ if (!key->key || !rsa->n || !rsa->e) {
+ goto oops;
+ }
+ if (!EVP_PKEY_set1_RSA(key->key, rsa))
+ goto oops;
+#endif
+ }
+ /* we are done */
+ return ((ssize_t)len);
+oops:
+#ifdef USE_BEARSSL
+ free(key->key);
+ key->key = NULL;
+#else
+ if (rsa)
+ RSA_free(rsa);
+ if (key->key) {
+ EVP_PKEY_free(key->key);
+ key->key = NULL;
+ }
+#endif
+ return (-1);
+}
+
+static OpenPGP_key *
+load_key_buf(unsigned char *buf, size_t nbytes)
+{
+ unsigned char *data = NULL;
+ unsigned char *ptr;
+ ssize_t rc;
+ int tag;
+ OpenPGP_key *key;
+
+ if (!buf)
+ return (NULL);
+
+ initialize();
+
+ if (!(buf[0] & OPENPGP_TAG_ISTAG)) {
+ /* Note: we do *not* free data */
+ data = dearmor((char *)buf, nbytes, &nbytes);
+ ptr = data;
+ } else
+ ptr = buf;
+ key = NEW(OpenPGP_key);
+ if (key) {
+ rc = decode_packet(0, &ptr, nbytes, (decoder_t)decode_key,
+ key);
+ if (rc < 0) {
+ free(key);
+ key = NULL;
+ } else if (rc > 8) {
+ int isnew, ltype;
+
+ tag = decode_tag(ptr, &isnew, &ltype);
+ if (tag == 13) {
+ key->user = NEW(OpenPGP_user);
+ rc = decode_packet(0, &ptr, (size_t)rc,
+ (decoder_t)decode_user, key->user);
+ }
+ }
+ }
+ return (key);
+}
+
+static LIST_HEAD(, OpenPGP_key_) trust_list;
+
+/**
+ * @brief add a key to our list
+ */
+void
+openpgp_trust_add(OpenPGP_key *key)
+{
+ static int once = 0;
+
+ if (!once) {
+ once = 1;
+
+ LIST_INIT(&trust_list);
+ }
+ if (key && openpgp_trust_get(key->id) == NULL) {
+ if (ve_anchor_verbose_get())
+ printf("openpgp_trust_add(%s)\n", key->id);
+ LIST_INSERT_HEAD(&trust_list, key, entries);
+ }
+}
+
+/**
+ * @brief add trust anchor from buf
+ */
+int
+openpgp_trust_add_buf(unsigned char *buf, size_t nbytes)
+{
+ OpenPGP_key *key;
+
+ if ((key = load_key_buf(buf, nbytes))) {
+ openpgp_trust_add(key);
+ }
+ return (key != NULL);
+}
+
+
+/**
+ * @brief if keyID is in our list clobber it
+ *
+ * @return true if keyID removed
+ */
+int
+openpgp_trust_revoke(const char *keyID)
+{
+ OpenPGP_key *key, *tkey;
+
+ openpgp_trust_add(NULL); /* initialize if needed */
+
+ LIST_FOREACH(key, &trust_list, entries) {
+ if (strcmp(key->id, keyID) == 0) {
+ tkey = key;
+ LIST_REMOVE(tkey, entries);
+ printf("openpgp_trust_revoke(%s)\n", key->id);
+ memset(key, 0, sizeof(OpenPGP_key));
+ free(key);
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/**
+ * @brief if keyID is in our list return the key
+ *
+ * @return key or NULL
+ */
+OpenPGP_key *
+openpgp_trust_get(const char *keyID)
+{
+ OpenPGP_key *key;
+
+ openpgp_trust_add(NULL); /* initialize if needed */
+
+ LIST_FOREACH(key, &trust_list, entries) {
+ if (strcmp(key->id, keyID) == 0)
+ return (key);
+ }
+ return (NULL);
+}
+
+/**
+ * @brief load a key from file
+ */
+OpenPGP_key *
+load_key_file(const char *kfile)
+{
+ unsigned char *data = NULL;
+ size_t n;
+ OpenPGP_key *key;
+
+ data = read_file(kfile, &n);
+ key = load_key_buf(data, n);
+ free(data);
+ openpgp_trust_add(key);
+ return (key);
+}
+
+#ifdef HAVE_TA_ASC_H
+#include <ta_asc.h>
+#endif
+
+#ifndef _STANDALONE
+/* we can lookup keyID in filesystem */
+
+static const char *trust_store[] = {
+ "/var/db/trust",
+ "/etc/db/trust",
+ NULL,
+};
+
+/**
+ * @brief lookup key id in trust store
+ *
+ */
+static OpenPGP_key *
+load_trusted_key_id(const char *keyID)
+{
+ char kfile[MAXPATHLEN];
+ const char **tp;
+ size_t n;
+
+ for (tp = trust_store; *tp; tp++) {
+ n = (size_t)snprintf(kfile, sizeof(kfile), "%s/%s", *tp, keyID);
+ if (n >= sizeof(kfile))
+ return (NULL);
+ if (access(kfile, R_OK) == 0) {
+ return (load_key_file(kfile));
+ }
+ }
+ return (NULL);
+}
+#endif
+
+/**
+ * @brief return key if trusted
+ */
+OpenPGP_key *
+load_key_id(const char *keyID)
+{
+ OpenPGP_key *key;
+
+ key = openpgp_trust_get(keyID);
+#ifndef _STANDALONE
+ if (!key)
+ key = load_trusted_key_id(keyID);
+#endif
+ DEBUG_PRINTF(2, ("load_key_id(%s): %s\n", keyID, key ? "found" : "nope"));
+ return (key);
+}
+
+/**
+ * @brief initialize our internal trust store if any
+ */
+int
+openpgp_trust_init(void)
+{
+ static int once = -1;
+#ifdef HAVE_TA_ASC
+ OpenPGP_key *key;
+ const char **tp;
+ char *cp;
+ size_t n;
+#endif
+
+ if (once < 0) {
+ once = 0;
+#ifdef HAVE_TA_ASC
+ for (tp = ta_ASC; *tp; tp++) {
+ if ((cp = strdup(*tp))) {
+ n = strlen(cp);
+ key = load_key_buf((unsigned char *)cp, n);
+ free(cp);
+ if (key) {
+ openpgp_trust_add(key);
+ once++;
+ }
+ }
+ }
+#endif
+ }
+ return (once);
+}
+
+/**
+ * @brief test that we can verify a signature
+ *
+ * Unlike X.509 certificates, we only support RSA keys
+ * so we stop after first successful signature verification
+ * (which should also be the first attempt ;-)
+ */
+int
+openpgp_self_tests(void)
+{
+ static int rc = -1; /* remember result */
+#ifdef HAVE_VC_ASC
+ const char **vp, **tp;
+ char *fdata, *sdata = NULL;
+ size_t fbytes, sbytes;
+
+ if (openpgp_trust_init() > 0) {
+ for (tp = ta_ASC, vp = vc_ASC; *tp && *vp && rc; tp++, vp++) {
+ if ((fdata = strdup(*tp)) &&
+ (sdata = strdup(*vp))) {
+ fbytes = strlen(fdata);
+ sbytes = strlen(sdata);
+ rc = openpgp_verify("ta_ASC",
+ (unsigned char *)fdata, fbytes,
+ (unsigned char *)sdata, sbytes, 0);
+ printf("Testing verify OpenPGP signature:\t\t%s\n",
+ rc ? "Failed" : "Passed");
+ }
+ free(fdata);
+ free(sdata);
+ }
+ }
+#endif
+ return (rc);
+}