Index: head/lib/libsecureboot/openpgp/opgp_key.c =================================================================== --- head/lib/libsecureboot/openpgp/opgp_key.c (revision 347979) +++ head/lib/libsecureboot/openpgp/opgp_key.c (revision 347980) @@ -1,369 +1,372 @@ /*- * 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); } } } - free(data); 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) + if (key) { + DEBUG_PRINTF(2, ("openpgp_trust_add(%s)\n", key->id)); LIST_INSERT_HEAD(&trust_list, key, entries); + } } /** * @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); } #include #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); } Index: head/lib/libsecureboot/openpgp/opgp_sig.c =================================================================== --- head/lib/libsecureboot/openpgp/opgp_sig.c (revision 347979) +++ head/lib/libsecureboot/openpgp/opgp_sig.c (revision 347980) @@ -1,484 +1,485 @@ /*- * 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. */ /* * RCSid: * from: signer.c,v 1.10 2018/03/23 01:14:30 sjg * * @(#) Copyright (c) 2012 Simon J. Gerraty * * This file is provided in the hope that it will * be of use. There is absolutely NO WARRANTY. * Permission to copy, redistribute or otherwise * use this file is hereby granted provided that * the above copyright notice and this notice are * left intact. * * Please send copies of changes and bug-fixes to: * sjg@crufty.net */ #include __FBSDID("$FreeBSD$"); #include "../libsecureboot-priv.h" #ifdef _STANDALONE #define warnx printf #else #include #include #include #include #include #include #include #include #endif #include "decode.h" #include "packet.h" #ifdef USE_BEARSSL #define get_error_string ve_error_get void initialize (void) { #ifdef _STANDALONE ve_trust_init(); #endif } #else #include /** * @brief intialize OpenSSL */ void initialize(void) { static int once; if (once) return); once = 1; //CRYPTO_malloc_init(); ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); } /** * @brief * last error from OpenSSL as a string */ char * get_error_string(void) { initialize(); return (ERR_error_string(ERR_get_error(), NULL)); } #endif /** * @brief decode a signature packet * * We only support RSA * * @sa rfc4880:5.2 */ ssize_t decode_sig(int tag, unsigned char **pptr, size_t len, OpenPGP_sig *sig) { unsigned char *ptr; unsigned char *pgpbytes; unsigned char *sp; int version; int hcount = 0; int ucount = 0; int stag = 0; int n; n = tag; /* avoid unused */ /* * We need to keep a reference to the packet bytes * as these form part of the signature data. * * @sa rfc4880:5.2.4 */ pgpbytes = ptr = *pptr; version = *ptr++; if (version == 3) { ptr++; sig->pgpbytes = malloc(5); if (!sig->pgpbytes) return (-1); memcpy(sig->pgpbytes, ptr, 5); sig->pgpbytes_len = 5; sig->sig_type = *ptr++; ptr += 4; sig->key_id = octets2hex(ptr, 8); ptr += 8; sig->sig_alg = *ptr++; sig->hash_alg = *ptr++; } else if (version == 4) { sig->sig_type = *ptr++; sig->sig_alg = *ptr++; sig->hash_alg = *ptr++; hcount = octets2i(ptr, 2); ptr += 2; sig->pgpbytes_len = (size_t)hcount + 6; sig->pgpbytes = malloc(sig->pgpbytes_len + 6); if (!sig->pgpbytes) return (-1); memcpy(sig->pgpbytes, pgpbytes, sig->pgpbytes_len); sp = &sig->pgpbytes[sig->pgpbytes_len]; *sp++ = 4; *sp++ = 255; memcpy(sp, i2octets(4, (int)sig->pgpbytes_len), 4); sig->pgpbytes_len += 6; while (hcount > 0) { sp = decode_subpacket(&ptr, &stag, &n); hcount -= n; /* can check stag to see if we care */ } ucount = octets2i(ptr, 2); ptr += 2; while (ucount > 0) { sp = decode_subpacket(&ptr, &stag, &n); ucount -= n; /* can check stag to see if we care */ if (stag == 16) { free(sig->key_id); sig->key_id = octets2hex(sp, 8); } } } else return (-1); ptr += 2; /* skip hash16 */ if (sig->sig_alg == 1) { /* RSA */ sig->sig = decode_mpi(&ptr, &sig->sig_len); } /* we are done */ return ((ssize_t)len); } /** * @brief map OpenPGP hash algorithm id's to name * * @sa rfc4880:9.4 */ static struct hash_alg_map { int halg; const char *hname; } hash_algs[] = { {1, "md5"}, {2, "sha1"}, {8, "sha256"}, {9, "sha384"}, {10, "sha512"}, {11, "sha224"}, {0, NULL}, }; static const char * get_hname(int hash_alg) { struct hash_alg_map *hmp; for (hmp = hash_algs; hmp->halg > 0; hmp++) { if (hmp->halg == hash_alg) return (hmp->hname); } return (NULL); } /* lifted from signer.c */ /** * @brief verify a digest * * The public key, digest name, file and signature data. * * @return 1 on success 0 on failure, -1 on error */ #ifndef USE_BEARSSL static int verify_digest (EVP_PKEY *pkey, const char *digest, unsigned char *mdata, size_t mlen, unsigned char *sdata, size_t slen) { EVP_MD_CTX ctx; const EVP_MD *md = NULL; EVP_PKEY_CTX *pctx = NULL; int rc = 0; int i = -1; initialize(); md = EVP_get_digestbyname(digest); EVP_DigestInit(&ctx, md); pctx = EVP_PKEY_CTX_new(pkey, NULL); if (!pctx) goto fail; if (EVP_PKEY_verify_init(pctx) <= 0) goto fail; if (EVP_PKEY_CTX_set_signature_md(pctx, ctx.digest) <= 0) goto fail; i = EVP_PKEY_verify(pctx, sdata, slen, mdata, mlen); if (i >= 0) rc = i; fail: EVP_PKEY_CTX_free(pctx); return (rc); } #endif /** * @brief verify OpenPGP signed file * * * @param[in] filename * used to determine the signature name * * @param[in] fdata * content of filename * * @param[in] fbytes * of fdata * * @param[in] sdata * content of signature * * @param[in] sbytes * of sdata * * @param[in] flags * * @return 0 on success */ int openpgp_verify(const char *filename, unsigned char *fdata, size_t fbytes, unsigned char *sdata, size_t sbytes, int flags) { OpenPGP_key *key; OpenPGP_sig *sig; #ifdef USE_BEARSSL const br_hash_class *md; br_hash_compat_context mctx; const unsigned char *hash_oid; #else const EVP_MD *md = NULL; EVP_MD_CTX mctx; #endif unsigned char mdata[64]; unsigned char *ptr; unsigned char *ddata = NULL; const char *hname; size_t mlen; int rc = -1; initialize(); sig = NEW(OpenPGP_sig); if (!sdata || !sig) { warnx("cannot verify %s", filename); goto oops; } if (!(sdata[0] & OPENPGP_TAG_ISTAG)) sdata = ddata = dearmor((char *)sdata, sbytes, &sbytes); ptr = sdata; rc = decode_packet(2, &ptr, sbytes, (decoder_t)decode_sig, sig); + DEBUG_PRINTF(2, ("rc=%d keyID=%s\n", rc, sig->key_id ? sig->key_id : "?")); if (rc == 0 && sig->key_id) { key = load_key_id(sig->key_id); if (!key) { warnx("cannot find key-id: %s", sig->key_id); rc = -1; } else if (!(hname = get_hname(sig->hash_alg))) { warnx("unsupported hash algorithm: %d", sig->hash_alg); rc = -1; } else { /* * Hash fdata according to the OpenPGP recipe * * @sa rfc4880:5.2.4 */ #ifdef USE_BEARSSL switch (sig->hash_alg) { /* see hash_algs above */ case 2: /* sha1 */ md = &br_sha1_vtable; mlen = br_sha1_SIZE; hash_oid = BR_HASH_OID_SHA1; break; case 8: /* sha256 */ md = &br_sha256_vtable; mlen = br_sha256_SIZE; hash_oid = BR_HASH_OID_SHA256; break; default: warnx("unsupported hash algorithm: %s", hname); goto oops; } md->init(&mctx.vtable); md->update(&mctx.vtable, fdata, fbytes); md->update(&mctx.vtable, sig->pgpbytes, sig->pgpbytes_len); md->out(&mctx.vtable, mdata); rc = verify_rsa_digest(key->key, hash_oid, mdata, mlen, sig->sig, sig->sig_len); #else md = EVP_get_digestbyname(hname); EVP_DigestInit(&mctx, md); EVP_DigestUpdate(&mctx, fdata, fbytes); EVP_DigestUpdate(&mctx, sig->pgpbytes, sig->pgpbytes_len); mlen = sizeof(mdata); EVP_DigestFinal(&mctx,mdata,(unsigned int *)&mlen); rc = verify_digest(key->key, hname, mdata, mlen, sig->sig, sig->sig_len); #endif if (rc > 0) { if ((flags & 1)) printf("Verified %s signed by %s\n", filename, key->user ? key->user->name : "someone"); rc = 0; /* success */ } else if (rc == 0) { printf("Unverified %s: %s\n", filename, get_error_string()); rc = 1; } else { printf("Unverified %s\n", filename); } } } else { warnx("cannot decode signature for %s", filename); rc = -1; } oops: free(ddata); free(sig); return (rc); } #ifndef _STANDALONE /** * @brief list of extensions we handle * * ".asc" is preferred as it works seamlessly with openpgp */ static const char *sig_exts[] = { ".asc", ".pgp", ".psig", NULL, }; /** * @brief verify OpenPGP signed file * * * @param[in] filename * used to determine the signature name * * @param[in] fdata * content of filename * * @param[in] nbytes * of fdata * * @return */ int openpgp_verify_file(const char *filename, unsigned char *fdata, size_t nbytes) { char pbuf[MAXPATHLEN]; unsigned char *sdata; const char *sname = NULL; const char **ep; size_t sz; int n; for (ep = sig_exts; *ep; ep++) { n = snprintf(pbuf, sizeof(pbuf), "%s%s", filename, *ep); if (n >= (int)sizeof(pbuf)) { warnx("cannot form signature name for %s", filename); return (-1); } if (access(pbuf, R_OK) == 0) { sname = pbuf; break; } } if (!sname) { warnx("cannot find signature for %s", filename); return (-1); } sdata = read_file(sname, &sz); return (openpgp_verify(filename, fdata, nbytes, sdata, sz, 1)); } #endif /** * @brief verify OpenPGP signature * * @return content of signed file */ unsigned char * verify_asc(const char *sigfile, int flags) { char pbuf[MAXPATHLEN]; char *cp; size_t n; unsigned char *fdata, *sdata; size_t fbytes, sbytes; if ((sdata = read_file(sigfile, &sbytes))) { n = strlcpy(pbuf, sigfile, sizeof(pbuf)); if ((cp = strrchr(pbuf, '.'))) *cp = '\0'; if ((fdata = read_file(pbuf, &fbytes))) { if (openpgp_verify(pbuf, fdata, fbytes, sdata, sbytes, flags)) { free(fdata); fdata = NULL; } } } else fdata = NULL; free(sdata); return (fdata); }