/*- * 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 __FBSDID("$FreeBSD$"); #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 #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); }