/* * Copyright 2016-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #ifdef OPENSSL_NO_CT # error "CT is disabled" #endif #include #include #include #include #include #include "ct_local.h" SCT_CTX *SCT_CTX_new(OPENSSL_CTX *libctx, const char *propq) { SCT_CTX *sctx = OPENSSL_zalloc(sizeof(*sctx)); if (sctx == NULL) CTerr(CT_F_SCT_CTX_NEW, ERR_R_MALLOC_FAILURE); sctx->libctx = libctx; if (propq != NULL) { sctx->propq = OPENSSL_strdup(propq); if (sctx->propq == NULL) { CTerr(CT_F_SCT_CTX_NEW, ERR_R_MALLOC_FAILURE); OPENSSL_free(sctx); return NULL; } } return sctx; } void SCT_CTX_free(SCT_CTX *sctx) { if (sctx == NULL) return; EVP_PKEY_free(sctx->pkey); OPENSSL_free(sctx->pkeyhash); OPENSSL_free(sctx->ihash); OPENSSL_free(sctx->certder); OPENSSL_free(sctx->preder); OPENSSL_free(sctx->propq); OPENSSL_free(sctx); } /* * Finds the index of the first extension with the given NID in cert. * If there is more than one extension with that NID, *is_duplicated is set to * 1, otherwise 0 (unless it is NULL). */ static int ct_x509_get_ext(X509 *cert, int nid, int *is_duplicated) { int ret = X509_get_ext_by_NID(cert, nid, -1); if (is_duplicated != NULL) *is_duplicated = ret >= 0 && X509_get_ext_by_NID(cert, nid, ret) >= 0; return ret; } /* * Modifies a certificate by deleting extensions and copying the issuer and * AKID from the presigner certificate, if necessary. * Returns 1 on success, 0 otherwise. */ __owur static int ct_x509_cert_fixup(X509 *cert, X509 *presigner) { int preidx, certidx; int pre_akid_ext_is_dup, cert_akid_ext_is_dup; if (presigner == NULL) return 1; preidx = ct_x509_get_ext(presigner, NID_authority_key_identifier, &pre_akid_ext_is_dup); certidx = ct_x509_get_ext(cert, NID_authority_key_identifier, &cert_akid_ext_is_dup); /* An error occurred whilst searching for the extension */ if (preidx < -1 || certidx < -1) return 0; /* Invalid certificate if they contain duplicate extensions */ if (pre_akid_ext_is_dup || cert_akid_ext_is_dup) return 0; /* AKID must be present in both certificate or absent in both */ if (preidx >= 0 && certidx == -1) return 0; if (preidx == -1 && certidx >= 0) return 0; /* Copy issuer name */ if (!X509_set_issuer_name(cert, X509_get_issuer_name(presigner))) return 0; if (preidx != -1) { /* Retrieve and copy AKID encoding */ X509_EXTENSION *preext = X509_get_ext(presigner, preidx); X509_EXTENSION *certext = X509_get_ext(cert, certidx); ASN1_OCTET_STRING *preextdata; /* Should never happen */ if (preext == NULL || certext == NULL) return 0; preextdata = X509_EXTENSION_get_data(preext); if (preextdata == NULL || !X509_EXTENSION_set_data(certext, preextdata)) return 0; } return 1; } int SCT_CTX_set1_cert(SCT_CTX *sctx, X509 *cert, X509 *presigner) { unsigned char *certder = NULL, *preder = NULL; X509 *pretmp = NULL; int certderlen = 0, prederlen = 0; int idx = -1; int poison_ext_is_dup, sct_ext_is_dup; int poison_idx = ct_x509_get_ext(cert, NID_ct_precert_poison, &poison_ext_is_dup); /* Duplicate poison extensions are present - error */ if (poison_ext_is_dup) goto err; /* If *cert doesn't have a poison extension, it isn't a precert */ if (poison_idx == -1) { /* cert isn't a precert, so we shouldn't have a presigner */ if (presigner != NULL) goto err; certderlen = i2d_X509(cert, &certder); if (certderlen < 0) goto err; } /* See if cert has a precert SCTs extension */ idx = ct_x509_get_ext(cert, NID_ct_precert_scts, &sct_ext_is_dup); /* Duplicate SCT extensions are present - error */ if (sct_ext_is_dup) goto err; if (idx >= 0 && poison_idx >= 0) { /* * cert can't both contain SCTs (i.e. have an SCT extension) and be a * precert (i.e. have a poison extension). */ goto err; } if (idx == -1) { idx = poison_idx; } /* * If either a poison or SCT extension is present, remove it before encoding * cert. This, along with ct_x509_cert_fixup(), gets a TBSCertificate (see * RFC5280) from cert, which is what the CT log signed when it produced the * SCT. */ if (idx >= 0) { X509_EXTENSION *ext; /* Take a copy of certificate so we don't modify passed version */ pretmp = X509_dup(cert); if (pretmp == NULL) goto err; ext = X509_delete_ext(pretmp, idx); X509_EXTENSION_free(ext); if (!ct_x509_cert_fixup(pretmp, presigner)) goto err; prederlen = i2d_re_X509_tbs(pretmp, &preder); if (prederlen <= 0) goto err; } X509_free(pretmp); OPENSSL_free(sctx->certder); sctx->certder = certder; sctx->certderlen = certderlen; OPENSSL_free(sctx->preder); sctx->preder = preder; sctx->prederlen = prederlen; return 1; err: OPENSSL_free(certder); OPENSSL_free(preder); X509_free(pretmp); return 0; } __owur static int ct_public_key_hash(SCT_CTX *sctx, X509_PUBKEY *pkey, unsigned char **hash, size_t *hash_len) { int ret = 0; unsigned char *md = NULL, *der = NULL; int der_len; unsigned int md_len; EVP_MD *sha256 = EVP_MD_fetch(sctx->libctx, "SHA2-256", sctx->propq); if (sha256 == NULL) goto err; /* Reuse buffer if possible */ if (*hash != NULL && *hash_len >= SHA256_DIGEST_LENGTH) { md = *hash; } else { md = OPENSSL_malloc(SHA256_DIGEST_LENGTH); if (md == NULL) goto err; } /* Calculate key hash */ der_len = i2d_X509_PUBKEY(pkey, &der); if (der_len <= 0) goto err; if (!EVP_Digest(der, der_len, md, &md_len, sha256, NULL)) goto err; if (md != *hash) { OPENSSL_free(*hash); *hash = md; *hash_len = SHA256_DIGEST_LENGTH; } md = NULL; ret = 1; err: EVP_MD_free(sha256); OPENSSL_free(md); OPENSSL_free(der); return ret; } int SCT_CTX_set1_issuer(SCT_CTX *sctx, const X509 *issuer) { return SCT_CTX_set1_issuer_pubkey(sctx, X509_get_X509_PUBKEY(issuer)); } int SCT_CTX_set1_issuer_pubkey(SCT_CTX *sctx, X509_PUBKEY *pubkey) { return ct_public_key_hash(sctx, pubkey, &sctx->ihash, &sctx->ihashlen); } int SCT_CTX_set1_pubkey(SCT_CTX *sctx, X509_PUBKEY *pubkey) { EVP_PKEY *pkey = X509_PUBKEY_get(pubkey); if (pkey == NULL) return 0; if (!ct_public_key_hash(sctx, pubkey, &sctx->pkeyhash, &sctx->pkeyhashlen)) { EVP_PKEY_free(pkey); return 0; } EVP_PKEY_free(sctx->pkey); sctx->pkey = pkey; return 1; } void SCT_CTX_set_time(SCT_CTX *sctx, uint64_t time_in_ms) { sctx->epoch_time_in_ms = time_in_ms; }