diff options
Diffstat (limited to 'lib/libsecureboot/openpgp/opgp_key.c')
-rw-r--r-- | lib/libsecureboot/openpgp/opgp_key.c | 413 |
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, <ype); + 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); +} |