static apr_status_t crypto_digest_final()

in crypto/apr_crypto_openssl.c [1581:1710]


static apr_status_t crypto_digest_final(apr_crypto_digest_t *digest)
{
    apr_status_t status = APR_SUCCESS;

    switch (digest->key->rec->ktype) {

    case APR_CRYPTO_KTYPE_HASH: {
        switch (digest->rec->dtype) {
        case APR_CRYPTO_DTYPE_HASH: {
            unsigned int len = EVP_MD_CTX_size(digest->mdCtx);

            /* must we allocate the output buffer from a pool? */
            if (!digest->rec->d.hash.s || digest->rec->d.hash.slen != len) {
                digest->rec->d.hash.slen = len;
                digest->rec->d.hash.s = apr_pcalloc(digest->pool, len);
                if (!digest->rec->d.hash.s) {
                    return APR_ENOMEM;
                }
            }

            /* then, determine the signature */
            if (EVP_DigestFinal_ex(digest->mdCtx, digest->rec->d.hash.s, &len)
                    == 0) {
                OPENSSL_cleanse(digest->rec->d.hash.s,
                                digest->rec->d.hash.slen);
                status = APR_ECRYPT;
            }

            break;
        }
        default:
            status = APR_ENODIGEST;
        }

        break;
    }
    case APR_CRYPTO_KTYPE_HMAC:
    case APR_CRYPTO_KTYPE_CMAC: {
        size_t len;

        /* first, determine the signature length */
#if APR_USE_OPENSSL_PRE_3_0_API
        if (!EVP_DigestSignFinal(digest->mdCtx, NULL, &len)) {
            status = APR_ECRYPT;
        }
#else
        if (!EVP_MAC_final(digest->macCtx, NULL, &len, 0)) {
            status = APR_ECRYPT;
        }
#endif
        if (status == APR_SUCCESS) {
            switch (digest->rec->dtype) {
            case APR_CRYPTO_DTYPE_SIGN: {
                /* must we allocate the output buffer from a pool? */
                if (!digest->rec->d.sign.s || digest->rec->d.sign.slen != len) {
                    digest->rec->d.sign.slen = len;
                    digest->rec->d.sign.s = apr_pcalloc(digest->pool, len);
                    if (!digest->rec->d.sign.s) {
                        return APR_ENOMEM;
                    }
                }

                /* then, determine the signature */
#if APR_USE_OPENSSL_PRE_3_0_API
                if (!EVP_DigestSignFinal(digest->mdCtx,
                                         digest->rec->d.sign.s, &len)) {
                    status = APR_ECRYPT;
                }
#else
                if (!EVP_MAC_final(digest->macCtx,
                                   digest->rec->d.sign.s, &len, len)) {
                    status = APR_ECRYPT;
                }
#endif
                if (status != APR_SUCCESS) {
                    OPENSSL_cleanse(digest->rec->d.sign.s,
                                    digest->rec->d.sign.slen);
                }

                break;
            }
            case APR_CRYPTO_DTYPE_VERIFY: {
                /* must we allocate the output buffer from a pool? */
                if (!digest->rec->d.verify.s
                        || digest->rec->d.verify.slen != len) {
                    digest->rec->d.verify.slen = len;
                    digest->rec->d.verify.s = apr_pcalloc(digest->pool, len);
                    if (!digest->rec->d.verify.s) {
                        return APR_ENOMEM;
                    }
                }

                /* then, determine the signature */
#if APR_USE_OPENSSL_PRE_3_0_API
                if (!EVP_DigestSignFinal(digest->mdCtx,
                                         digest->rec->d.verify.s, &len)) {
                    status = APR_ECRYPT;
                }
#else
                if (!EVP_MAC_final(digest->macCtx,
                                   digest->rec->d.verify.s, &len, len)) {
                    status = APR_ECRYPT;
                }
#endif
                if (status == APR_SUCCESS
                    && (len != digest->rec->d.verify.vlen
                        || CRYPTO_memcmp(digest->rec->d.verify.v,
                                         digest->rec->d.verify.s, len))) {
                    status = APR_ENOVERIFY;
                }
                if (status != APR_SUCCESS) {
                    OPENSSL_cleanse(digest->rec->d.verify.s,
                                    digest->rec->d.verify.slen);
                }

                break;
            }
            default:
                status = APR_ENODIGEST;
            }
        }

        break;
    }
    default:
        status = APR_EINVAL;
    }

    return status;
}