From 57c766713945dc11fc0309f04ed84744580f1e72 Mon Sep 17 00:00:00 2001
From: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
Date: Sat, 18 Feb 2023 10:47:53 +0100
Subject: libclamav: Use OpenSSL' BN instead tomfastmath.

Use OpenSSL's big number/ multiprecision integer arithmetics
functionality to replace tomfastmath.

This is a first shot at doing just this. Further improvement could be
use more RSA-signature verification from OpenSSL in crtmgr_rsa_verify()
and less self parsing.
_padding_check_PKCS1_type_1() has been borrowed from OpenSSL to make
further replacments easier.

Patch-Name: libclamav-Use-OpenSSL-BN-instead-tomfastmath.patch
Signed-off-by: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
---
 libclamav/CMakeLists.txt |   3 +-
 libclamav/asn1.c         |  33 +++-
 libclamav/bignum.h       |  14 --
 libclamav/crtmgr.c       | 405 ++++++++++++++++++++++++++-------------
 libclamav/crtmgr.h       |  14 +-
 libclamav/dsig.c         | 175 ++++++++++++-----
 libclamav/dsig.h         |   2 +-
 libclamav/readdb.c       |  24 ++-
 libclamav/textnorm.c     |   1 -
 libclamav/xdp.c          |   1 -
 10 files changed, 443 insertions(+), 229 deletions(-)
 delete mode 100644 libclamav/bignum.h

diff --git a/libclamav/CMakeLists.txt b/libclamav/CMakeLists.txt
index dbd084c..a60955e 100644
--- a/libclamav/CMakeLists.txt
+++ b/libclamav/CMakeLists.txt
@@ -250,8 +250,7 @@ target_sources( tomsfastmath
         tomsfastmath/sqr/fp_sqr_comba_generic.c
         tomsfastmath/sqr/fp_sqr_comba_small_set.c
         tomsfastmath/sqr/fp_sqrmod.c
-    PUBLIC
-        bignum.h )
+        )
 target_include_directories( tomsfastmath
     PRIVATE
         ${CMAKE_BINARY_DIR}
diff --git a/libclamav/asn1.c b/libclamav/asn1.c
index 1eec3b0..880dbc5 100644
--- a/libclamav/asn1.c
+++ b/libclamav/asn1.c
@@ -24,10 +24,10 @@
 #endif
 
 #include <time.h>
+#include <openssl/bn.h>
 
 #include "clamav.h"
 #include "asn1.h"
-#include "bignum.h"
 #include "matcher-hash.h"
 
 /* --------------------------------------------------------------------------- OIDS */
@@ -695,7 +695,8 @@ static int asn1_get_rsa_pubkey(fmap_t *map, const void **asn1data, unsigned int
         return 1;
     }
 
-    fp_read_unsigned_bin(&x509->n, obj.content, avail2);
+    if (!BN_bin2bn(obj.content, avail2, x509->n))
+        return 1;
 
     if (asn1_expect_objtype(map, obj.next, &avail, &obj, ASN1_TYPE_INTEGER)) /* INTEGER - exp */
         return 1;
@@ -712,7 +713,8 @@ static int asn1_get_rsa_pubkey(fmap_t *map, const void **asn1data, unsigned int
         return 1;
     }
 
-    fp_read_unsigned_bin(&x509->e, obj.content, obj.size);
+    if (!BN_bin2bn(obj.content, obj.size, x509->e))
+        return 1;
 
     return 0;
 }
@@ -738,9 +740,12 @@ static int asn1_get_x509(fmap_t *map, const void **asn1data, unsigned int *size,
     int ret = ASN1_GET_X509_UNRECOVERABLE_ERROR;
     unsigned int version;
 
-    cli_crt_init(&x509);
-
     do {
+        if (cli_crt_init(&x509) < 0) {
+            cli_dbgmsg("asn1_get_x509: failed to initialize x509.\n");
+            break;
+        }
+
         if (asn1_expect_objtype(map, *asn1data, size, &crt, ASN1_TYPE_SEQUENCE)) { /* SEQUENCE */
             cli_dbgmsg("asn1_get_x509: expected SEQUENCE at the x509 start\n");
             break;
@@ -1107,7 +1112,8 @@ static int asn1_get_x509(fmap_t *map, const void **asn1data, unsigned int *size,
             break;
         }
 
-        fp_read_unsigned_bin(&x509.sig, obj.content, obj.size);
+        if (!BN_bin2bn(obj.content, obj.size, x509.sig))
+            break;
 
         if (crt.size) {
             cli_dbgmsg("asn1_get_x509: found unexpected extra data in signature\n");
@@ -1404,6 +1410,8 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t
     void *hash_ctx;
     int result;
     cl_error_t ret = CL_EPARSE;
+    char *mod      = NULL;
+    char *exp      = NULL;
 
     cli_dbgmsg("in asn1_parse_mscat\n");
 
@@ -1558,11 +1566,10 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t
                     while (x509) {
                         char raw_issuer[CRT_RAWMAXLEN * 2 + 1], raw_subject[CRT_RAWMAXLEN * 2 + 1], raw_serial[CRT_RAWMAXLEN * 3 + 1];
                         char issuer[SHA1_HASH_SIZE * 2 + 1], subject[SHA1_HASH_SIZE * 2 + 1], serial[SHA1_HASH_SIZE * 2 + 1];
-                        char mod[1024 + 1], exp[1024 + 1];
-                        int j = 1024;
+                        int j;
 
-                        fp_toradix_n(&x509->n, mod, 16, j + 1);
-                        fp_toradix_n(&x509->e, exp, 16, j + 1);
+                        mod = BN_bn2hex(x509->n);
+                        exp = BN_bn2hex(x509->e);
                         memset(raw_issuer, 0, CRT_RAWMAXLEN * 2 + 1);
                         memset(raw_subject, 0, CRT_RAWMAXLEN * 2 + 1);
                         memset(raw_serial, 0, CRT_RAWMAXLEN * 2 + 1);
@@ -1594,6 +1601,10 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t
                         cli_dbgmsg("  raw_issuer: %s\n", raw_issuer);
 
                         x509 = x509->next;
+                        OPENSSL_free(mod);
+                        OPENSSL_free(exp);
+                        mod = NULL;
+                        exp = NULL;
                     }
                     x509 = newcerts.crts;
                 }
@@ -2149,6 +2160,8 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t
     } while (0);
 
 finish:
+    OPENSSL_free(mod);
+    OPENSSL_free(exp);
     if (CL_EPARSE == ret) {
         cli_dbgmsg("asn1_parse_mscat: failed to parse authenticode section\n");
     }
diff --git a/libclamav/bignum.h b/libclamav/bignum.h
deleted file mode 100644
index a1c6d6e..0000000
--- a/libclamav/bignum.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef BIGNUM_H_
-#define BIGNUM_H_
-
-#if HAVE_CONFIG_H
-#include "clamav-config.h"
-#endif
-
-#if HAVE_SYSTEM_TOMSFASTMATH
-#include <tfm.h>
-#else
-#include "tomsfastmath/headers/tfm.h"
-#endif
-
-#endif
diff --git a/libclamav/crtmgr.c b/libclamav/crtmgr.c
index 571b1a7..3943e14 100644
--- a/libclamav/crtmgr.c
+++ b/libclamav/crtmgr.c
@@ -42,20 +42,39 @@
 #define OID_2_16_840_1_101_3_4_2_3 "\x60\x86\x48\x01\x65\x03\x04\x02\x03"
 #define OID_sha512 OID_2_16_840_1_101_3_4_2_3
 
-#define FP_INIT_MULTI(a, b, c) (fp_init(a), fp_init(b), fp_init(c))
-#define FP_CLEAR_MULTI(...)
+static int cli_crt_init_fps(cli_crt *x509)
+{
+    x509->n   = BN_new();
+    x509->e   = BN_new();
+    x509->sig = BN_new();
 
-void cli_crt_init(cli_crt *x509)
+    if (!x509->n || !x509->e || !x509->sig) {
+        BN_free(x509->n);
+        BN_free(x509->e);
+        BN_free(x509->sig);
+
+        x509->n   = NULL;
+        x509->e   = NULL;
+        x509->sig = NULL;
+        return -1;
+    }
+    return 0;
+}
+
+int cli_crt_init(cli_crt *x509)
 {
     memset(x509, 0, sizeof(*x509));
-
-    // FP_INIT_MULTI is a memset for each and cannot fail.
-    FP_INIT_MULTI(&x509->n, &x509->e, &x509->sig);
+    return cli_crt_init_fps(x509);
 }
 
 void cli_crt_clear(cli_crt *x509)
 {
-    FP_CLEAR_MULTI(&x509->n, &x509->e, &x509->sig);
+    BN_free(x509->n);
+    BN_free(x509->e);
+    BN_free(x509->sig);
+    x509->n   = NULL;
+    x509->e   = NULL;
+    x509->sig = NULL;
 }
 
 /* Look for an existing certificate in the trust store `m`.  This search allows
@@ -118,7 +137,7 @@ cli_crt *crtmgr_trust_list_lookup(crtmgr *m, cli_crt *x509, int crb_crts_only)
             if (x509->hashtype != i->hashtype ||
                 memcmp(x509->issuer, i->issuer, sizeof(i->issuer)) ||
                 x509->ignore_serial != i->ignore_serial ||
-                fp_cmp(&x509->e, &i->e)) {
+                BN_cmp(x509->e, i->e)) {
                 continue;
             }
         }
@@ -135,7 +154,7 @@ cli_crt *crtmgr_trust_list_lookup(crtmgr *m, cli_crt *x509, int crb_crts_only)
             (i->codeSign | x509->codeSign) == i->codeSign &&
             (i->timeSign | x509->timeSign) == i->timeSign &&
             !memcmp(x509->subject, i->subject, sizeof(i->subject)) &&
-            !fp_cmp(&x509->n, &i->n)) {
+            !BN_cmp(x509->n, i->n)) {
             return i;
         }
     }
@@ -166,7 +185,7 @@ cli_crt *crtmgr_block_list_lookup(crtmgr *m, cli_crt *x509)
 
         if (!i->isBlocked ||
             memcmp(i->subject, x509->subject, sizeof(i->subject)) ||
-            fp_cmp(&x509->n, &i->n)) {
+            BN_cmp(x509->n, i->n)) {
             continue;
         }
 
@@ -191,37 +210,51 @@ cli_crt *crtmgr_lookup(crtmgr *m, cli_crt *x509)
     }
 }
 
-int crtmgr_add(crtmgr *m, cli_crt *x509)
+bool crtmgr_add(crtmgr *m, cli_crt *x509)
 {
-    cli_crt *i;
+    bool failed = true;
+    cli_crt *i  = NULL;
 
     if (x509->isBlocked) {
         if (crtmgr_block_list_lookup(m, x509)) {
             cli_dbgmsg("crtmgr_add: duplicate blocked certificate detected - not adding\n");
-            return 0;
+            failed = false;
+            goto done;
         }
     } else {
         if (crtmgr_trust_list_lookup(m, x509, 0)) {
             cli_dbgmsg("crtmgr_add: duplicate trusted certificate detected - not adding\n");
-            return 0;
+            failed = false;
+            goto done;
         }
     }
 
     i = cli_malloc(sizeof(*i));
-    if (!i)
-        return 1;
+    if (i == NULL) {
+        goto done;
+    }
 
-    // FP_INIT_MULTI is a memset for each and cannot fail.
-    FP_INIT_MULTI(&i->n, &i->e, &i->sig);
+    if (cli_crt_init_fps(i) < 0) {
+        goto done;
+    }
 
-    fp_copy(&x509->n, &i->n);
-    fp_copy(&x509->e, &i->e);
-    fp_copy(&x509->sig, &i->sig);
+    if (!BN_copy(i->n, x509->n)) {
+        goto done;
+    }
+    if (!BN_copy(i->e, x509->e)) {
+        goto done;
+    }
+    if (!BN_copy(i->sig, x509->sig)) {
+        goto done;
+    }
 
-    if ((x509->name))
+    if (x509->name) {
         i->name = strdup(x509->name);
-    else
+        if (!i->name)
+            goto done;
+    } else {
         i->name = NULL;
+    }
 
     memcpy(i->raw_subject, x509->raw_subject, sizeof(i->raw_subject));
     memcpy(i->raw_issuer, x509->raw_issuer, sizeof(i->raw_issuer));
@@ -240,12 +273,23 @@ int crtmgr_add(crtmgr *m, cli_crt *x509)
     i->isBlocked     = x509->isBlocked;
     i->next          = m->crts;
     i->prev          = NULL;
-    if (m->crts)
+    if (m->crts) {
         m->crts->prev = i;
+    }
     m->crts = i;
 
     m->items++;
-    return 0;
+
+    failed = false;
+    i      = NULL;
+
+done:
+    if (i != NULL) {
+        cli_crt_clear(i);
+        free(i);
+    }
+
+    return failed;
 }
 
 void crtmgr_init(crtmgr *m)
@@ -281,12 +325,133 @@ void crtmgr_free(crtmgr *m)
         crtmgr_del(m, m->crts);
 }
 
-static int crtmgr_rsa_verify(cli_crt *x509, fp_int *sig, cli_crt_hashtype hashtype, const uint8_t *refhash)
+static cl_error_t _padding_check_PKCS1_type_1(uint8_t **to, int *tlen,
+                                              uint8_t *from, unsigned int flen,
+                                              unsigned int num)
 {
-    int keylen = fp_unsigned_bin_size(&x509->n), siglen = fp_unsigned_bin_size(sig);
-    int ret, j, objlen, hashlen;
-    uint8_t d[513];
-    fp_int x;
+    int i, j;
+    unsigned char *p;
+
+    p = from;
+
+    /*
+     * The format is
+     * 00 || 01 || PS || 00 || D
+     * PS - padding string, at least 8 bytes of FF
+     * D  - data.
+     */
+
+    if (num < 11) /* RSA_PKCS1_PADDING_SIZE */
+        return CL_EPARSE;
+
+    /* Accept inputs with and without the leading 0-byte. */
+    if (num == flen) {
+        if ((*p++) != 0x00) {
+            cli_dbgmsg("%s: Bad padding\n", __func__);
+            return CL_EPARSE;
+        }
+        flen--;
+    }
+
+    if ((num != (flen + 1)) || (*(p++) != 0x01)) {
+        cli_dbgmsg("%s: Bad block type\n", __func__);
+        return CL_EPARSE;
+    }
+
+    /* scan over padding data */
+    j = flen - 1; /* one for type. */
+    for (i = 0; i < j; i++) {
+        if (*p != 0xff) { /* should decrypt to 0xff */
+            if (*p == 0) {
+                p++;
+                break;
+            } else {
+                cli_dbgmsg("%s: Bad header\n", __func__);
+                return CL_EPARSE;
+            }
+        }
+        p++;
+    }
+
+    if (i == j) {
+        cli_dbgmsg("%s: Bad header\n", __func__);
+        return CL_EPARSE;
+    }
+
+    if (i < 8) {
+        cli_dbgmsg("%s: Bad padding\n", __func__);
+        return CL_EPARSE;
+    }
+    i++; /* Skip over the '\0' */
+    j -= i;
+    *tlen = j;
+    *to   = p;
+
+    return CL_SUCCESS;
+}
+
+static cl_error_t crtmgr_get_recov_data(BIGNUM *sig, cli_crt *x509,
+                                        uint8_t **buffer, uint8_t **payload,
+                                        int *payload_len)
+{
+    BN_CTX *bnctx;
+    int pad_size;
+    int keylen;
+    uint8_t *d;
+    BIGNUM *x;
+    cl_error_t ret;
+
+    *buffer      = NULL;
+    *payload     = NULL;
+    *payload_len = 0;
+    ret          = CL_ERROR;
+
+    keylen = BN_num_bytes(x509->n);
+    bnctx  = BN_CTX_new();
+    if (!bnctx)
+        goto done;
+
+    x = BN_new();
+    if (!x)
+        goto done;
+
+    MALLOC(d, keylen);
+
+    if (!BN_mod_exp(x, sig, x509->e, x509->n, bnctx)) {
+        cli_warnmsg("crtmgr_rsa_verify: verification failed: BN_mod_exp failed.\n");
+        goto done;
+    }
+
+    pad_size = BN_bn2bin(x, d);
+    if (pad_size < 0) {
+        cli_dbgmsg("crtmgr_rsa_verify: buffer too small.\n");
+        goto done;
+    }
+
+    ret = _padding_check_PKCS1_type_1(payload, payload_len, d, pad_size, keylen);
+    if (ret != CL_SUCCESS) {
+        cli_dbgmsg("crtmgr_rsa_verify: RSA_padding_check_PKCS1_type_1() failed\n");
+        goto done;
+    }
+    *buffer = d;
+    d       = NULL;
+    ret     = CL_SUCCESS;
+
+done:
+    BN_CTX_free(bnctx);
+    BN_free(x);
+    free(d);
+    return ret;
+}
+
+static int crtmgr_rsa_verify(cli_crt *x509, BIGNUM *sig, cli_crt_hashtype hashtype, const uint8_t *refhash)
+{
+    int keylen = BN_num_bytes(x509->n), siglen = BN_num_bytes(sig);
+    int j, objlen, hashlen;
+    uint8_t *d;
+    uint8_t *buff;
+    int len;
+    cl_error_t ret;
 
     if (hashtype == CLI_SHA1RSA) {
         hashlen = SHA1_HASH_SIZE;
@@ -303,132 +468,100 @@ static int crtmgr_rsa_verify(cli_crt *x509, fp_int *sig, cli_crt_hashtype hashty
         return 1;
     }
 
-    fp_init(&x);
+    if (MAX(keylen, siglen) - MIN(keylen, siglen) > 1) {
+        cli_dbgmsg("crtmgr_rsa_verify: keylen and siglen differ by more than one\n");
+        return 1;
+    }
+
+    ret = crtmgr_get_recov_data(sig, x509, &buff, &d, &len);
+    if (ret != CL_SUCCESS)
+        return 1;
 
     do {
-        if (MAX(keylen, siglen) - MIN(keylen, siglen) > 1) {
-            cli_dbgmsg("crtmgr_rsa_verify: keylen and siglen differ by more than one\n");
+        j = 0;
+
+        if (len <= hashlen) {
+            cli_dbgmsg("crtmgr_rsa_verify: encountered len less than hashlen\n");
             break;
         }
-        if ((ret = fp_exptmod(sig, &x509->e, &x509->n, &x))) {
-            cli_warnmsg("crtmgr_rsa_verify: verification failed: fp_exptmod failed with %d\n", ret);
+        /* hash is asn1 der encoded */
+        /* SEQ { SEQ { OID, NULL }, OCTET STRING */
+        if (len < 2 || d[j] != 0x30 || d[j + 1] != len - 2) {
+            cli_dbgmsg("crtmgr_rsa_verify: unexpected hash to be ASN1 DER encoded.\n");
             break;
         }
-        if (fp_unsigned_bin_size(&x) != keylen - 1) {
-            cli_dbgmsg("crtmgr_rsa_verify: keylen-1 doesn't match expected size of exptmod result\n");
-            break;
-        }
-        if (((unsigned int)fp_unsigned_bin_size(&x)) > sizeof(d)) {
-            cli_dbgmsg("crtmgr_rsa_verify: exptmod result would overrun working buffer\n");
+        len -= 2;
+        j += 2;
+
+        if (len < 2 || d[j] != 0x30) {
+            cli_dbgmsg("crtmgr_rsa_verify: expected SEQUENCE at beginning of cert AlgorithmIdentifier\n");
             break;
         }
 
-        fp_to_unsigned_bin(&x, d);
+        objlen = d[j + 1];
 
-        if (*d != 1) { /* block type 1 */
-            cli_dbgmsg("crtmgr_rsa_verify: expected block type 1 at d[0]\n");
+        len -= 2;
+        j += 2;
+        if (len < objlen) {
+            cli_dbgmsg("crtmgr_rsa_verify: key length mismatch in ASN1 DER hash encoding\n");
             break;
         }
-
-        keylen -= 1; /* 0xff padding */
-        for (j = 1; j < keylen - 2; j++)
-            if (d[j] != 0xff)
-                break;
-        if (j == keylen - 2) {
-            cli_dbgmsg("crtmgr_rsa_verify: only encountered 0xFF padding parsing cert\n");
-            break;
-        }
-        if (d[j] != 0) { /* 0x00 separator */
-            cli_dbgmsg("crtmgr_rsa_verify: expected 0x00 separator\n");
-            break;
-        }
-
-        j++;
-        keylen -= j; /* asn1 size */
-
-        if (keylen < hashlen) {
-            cli_dbgmsg("crtmgr_rsa_verify: encountered keylen less than hashlen\n");
-            break;
-        }
-        if (keylen > hashlen) {
-            /* hash is asn1 der encoded */
-            /* SEQ { SEQ { OID, NULL }, OCTET STRING */
-            if (keylen < 2 || d[j] != 0x30 || d[j + 1] + 2 != keylen) {
-                cli_dbgmsg("crtmgr_rsa_verify: unexpected hash to be ASN1 DER encoded\n");
+        if (objlen == 9) {
+            // Check for OID type indicating a length of 5, OID_sha1, and the NULL type/value
+            if (hashtype != CLI_SHA1RSA || memcmp(&d[j], "\x06\x05" OID_sha1 "\x05\x00", 9)) {
+                cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
                 break;
             }
-            keylen -= 2;
-            j += 2;
-
-            if (keylen < 2 || d[j] != 0x30) {
-                cli_dbgmsg("crtmgr_rsa_verify: expected SEQUENCE at beginning of cert AlgorithmIdentifier\n");
+        } else if (objlen == 12) {
+            // Check for OID type indicating a length of 8, OID_md5, and the NULL type/value
+            if (hashtype != CLI_MD5RSA || memcmp(&d[j], "\x06\x08" OID_md5 "\x05\x00", 12)) {
+                cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
                 break;
             }
-
-            objlen = d[j + 1];
-
-            keylen -= 2;
-            j += 2;
-            if (keylen < objlen) {
-                cli_dbgmsg("crtmgr_rsa_verify: key length mismatch in ASN1 DER hash encoding\n");
-                break;
-            }
-            if (objlen == 9) {
-                // Check for OID type indicating a length of 5, OID_sha1, and the NULL type/value
-                if (hashtype != CLI_SHA1RSA || memcmp(&d[j], "\x06\x05" OID_sha1 "\x05\x00", 9)) {
-                    cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
+        } else if (objlen == 13) {
+            if (hashtype == CLI_SHA256RSA) {
+                // Check for OID type indicating a length of 9, OID_sha256, and the NULL type/value
+                if (0 != memcmp(&d[j], "\x06\x09" OID_sha256 "\x05\x00", 13)) {
+                    cli_dbgmsg("crtmgr_rsa_verify: invalid AlgorithmIdentifier block for SHA256 hash\n");
                     break;
                 }
-            } else if (objlen == 12) {
-                // Check for OID type indicating a length of 8, OID_md5, and the NULL type/value
-                if (hashtype != CLI_MD5RSA || memcmp(&d[j], "\x06\x08" OID_md5 "\x05\x00", 12)) {
-                    cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
+
+            } else if (hashtype == CLI_SHA384RSA) {
+                // Check for OID type indicating a length of 9, OID_sha384, and the NULL type/value
+                if (0 != memcmp(&d[j], "\x06\x09" OID_sha384 "\x05\x00", 13)) {
+                    cli_dbgmsg("crtmgr_rsa_verify: invalid AlgorithmIdentifier block for SHA384 hash\n");
                     break;
                 }
-            } else if (objlen == 13) {
-                if (hashtype == CLI_SHA256RSA) {
-                    // Check for OID type indicating a length of 9, OID_sha256, and the NULL type/value
-                    if (0 != memcmp(&d[j], "\x06\x09" OID_sha256 "\x05\x00", 13)) {
-                        cli_dbgmsg("crtmgr_rsa_verify: invalid AlgorithmIdentifier block for SHA256 hash\n");
-                        break;
-                    }
 
-                } else if (hashtype == CLI_SHA384RSA) {
-                    // Check for OID type indicating a length of 9, OID_sha384, and the NULL type/value
-                    if (0 != memcmp(&d[j], "\x06\x09" OID_sha384 "\x05\x00", 13)) {
-                        cli_dbgmsg("crtmgr_rsa_verify: invalid AlgorithmIdentifier block for SHA384 hash\n");
-                        break;
-                    }
-
-                } else if (hashtype == CLI_SHA512RSA) {
-                    // Check for OID type indicating a length of 9, OID_sha512, and the NULL type/value
-                    if (0 != memcmp(&d[j], "\x06\x09" OID_sha512 "\x05\x00", 13)) {
-                        cli_dbgmsg("crtmgr_rsa_verify: invalid AlgorithmIdentifier block for SHA512 hash\n");
-                        break;
-                    }
-
-                } else {
-                    cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
+            } else if (hashtype == CLI_SHA512RSA) {
+                // Check for OID type indicating a length of 9, OID_sha512, and the NULL type/value
+                if (0 != memcmp(&d[j], "\x06\x09" OID_sha512 "\x05\x00", 13)) {
+                    cli_dbgmsg("crtmgr_rsa_verify: invalid AlgorithmIdentifier block for SHA512 hash\n");
                     break;
                 }
+
             } else {
                 cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
                 break;
             }
-
-            keylen -= objlen;
-            j += objlen;
-            if (keylen < 2 || d[j] != 0x04 || d[j + 1] != hashlen) {
-                cli_dbgmsg("crtmgr_rsa_verify: hash length mismatch in ASN1 DER hash encoding\n");
-                break;
-            }
-            keylen -= 2;
-            j += 2;
-            if (keylen != hashlen) {
-                cli_dbgmsg("crtmgr_rsa_verify: extra data in the ASN1 DER hash encoding\n");
-                break;
-            }
+        } else {
+            cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
+            break;
         }
+
+        len -= objlen;
+        j += objlen;
+        if (len < 2 || d[j] != 0x04 || d[j + 1] != hashlen) {
+            cli_dbgmsg("crtmgr_rsa_verify: hash length mismatch in ASN1 DER hash encoding\n");
+            break;
+        }
+        j += 2;
+        len -= 2;
+        if (len != hashlen) {
+            cli_dbgmsg("crtmgr_rsa_verify: extra data in the ASN1 DER hash encoding\n");
+            break;
+        }
+
         if (memcmp(&d[j], refhash, hashlen)) {
             // This is a common error case if we are using crtmgr_rsa_verify to
             // determine whether we've found the right issuer certificate based
@@ -438,10 +571,12 @@ static int crtmgr_rsa_verify(cli_crt *x509, fp_int *sig, cli_crt_hashtype hashty
             break;
         }
 
+        free(buff);
         return 0;
 
     } while (0);
 
+    free(buff);
     return 1;
 }
 
@@ -469,7 +604,7 @@ cli_crt *crtmgr_verify_crt(crtmgr *m, cli_crt *x509)
         if (i->certSign &&
             !i->isBlocked &&
             !memcmp(i->subject, x509->issuer, sizeof(i->subject)) &&
-            !crtmgr_rsa_verify(i, &x509->sig, x509->hashtype, x509->tbshash)) {
+            !crtmgr_rsa_verify(i, x509->sig, x509->hashtype, x509->tbshash)) {
             int curscore;
             if ((x509->codeSign & i->codeSign) == x509->codeSign && (x509->timeSign & i->timeSign) == x509->timeSign)
                 return i;
@@ -493,16 +628,18 @@ cli_crt *crtmgr_verify_crt(crtmgr *m, cli_crt *x509)
 cli_crt *crtmgr_verify_pkcs7(crtmgr *m, const uint8_t *issuer, const uint8_t *serial, const void *signature, unsigned int signature_len, cli_crt_hashtype hashtype, const uint8_t *refhash, cli_vrfy_type vrfytype)
 {
     cli_crt *i;
-    fp_int sig;
+    BIGNUM *sig;
 
     if (signature_len < 1024 / 8 || signature_len > 4096 / 8 + 1) {
         cli_dbgmsg("crtmgr_verify_pkcs7: unsupported sig len: %u\n", signature_len);
         return NULL;
     }
 
-    fp_init(&sig);
+    sig = BN_new();
+    if (!sig)
+        return NULL;
 
-    fp_read_unsigned_bin(&sig, signature, signature_len);
+    BN_bin2bn(signature, signature_len, sig);
 
     for (i = m->crts; i; i = i->next) {
         if (vrfytype == VRFY_CODE && !i->codeSign)
@@ -511,13 +648,13 @@ cli_crt *crtmgr_verify_pkcs7(crtmgr *m, const uint8_t *issuer, const uint8_t *se
             continue;
         if (!memcmp(i->issuer, issuer, sizeof(i->issuer)) &&
             !memcmp(i->serial, serial, sizeof(i->serial))) {
-            if (!crtmgr_rsa_verify(i, &sig, hashtype, refhash)) {
+            if (!crtmgr_rsa_verify(i, sig, hashtype, refhash)) {
                 break;
             }
             cli_dbgmsg("crtmgr_verify_pkcs7: found cert with matching issuer and serial but RSA verification failed\n");
         }
     }
-
+    BN_free(sig);
     return i;
 }
 
diff --git a/libclamav/crtmgr.h b/libclamav/crtmgr.h
index e7699ba..6b4db9a 100644
--- a/libclamav/crtmgr.h
+++ b/libclamav/crtmgr.h
@@ -23,8 +23,8 @@
 #define __CRTMGR_H
 
 #include <time.h>
-
-#include "bignum.h"
+#include <stdbool.h>
+#include <openssl/bn.h>
 
 typedef enum { CLI_HASHTYPE_ANY, /* used by crts added from .CRB rules */
                CLI_SHA1RSA,
@@ -63,9 +63,9 @@ typedef struct cli_crt_t {
      * so it must have at least enough space for the largest hash in
      * cli_crt_hashtype */
     uint8_t tbshash[SHA512_HASH_SIZE];
-    fp_int n;
-    fp_int e;
-    fp_int sig;
+    BIGNUM *n;
+    BIGNUM *e;
+    BIGNUM *sig;
     int64_t not_before;
     int64_t not_after;
     cli_crt_hashtype hashtype;
@@ -82,11 +82,11 @@ typedef struct {
     unsigned int items;
 } crtmgr;
 
-void cli_crt_init(cli_crt *x509);
+int cli_crt_init(cli_crt *x509);
 void cli_crt_clear(cli_crt *x509);
 void crtmgr_init(crtmgr *m);
 void crtmgr_free(crtmgr *m);
-int crtmgr_add(crtmgr *m, cli_crt *x509);
+bool crtmgr_add(crtmgr *m, cli_crt *x509);
 cli_crt *crtmgr_lookup(crtmgr *m, cli_crt *x509);
 cli_crt *crtmgr_block_list_lookup(crtmgr *m, cli_crt *x509);
 cli_crt *crtmgr_trust_list_lookup(crtmgr *m, cli_crt *x509, int crb_crts_only);
diff --git a/libclamav/dsig.c b/libclamav/dsig.c
index c8825b2..59303f9 100644
--- a/libclamav/dsig.c
+++ b/libclamav/dsig.c
@@ -30,12 +30,12 @@
 #include <string.h>
 #include <stdlib.h>
 #include <ctype.h>
+#include <openssl/bn.h>
 
 #include "clamav.h"
 #include "others.h"
 #include "dsig.h"
 #include "str.h"
-#include "bignum.h"
 
 #ifndef _WIN32
 #include <sys/socket.h>
@@ -81,37 +81,83 @@ static char cli_ndecode(unsigned char value)
     return -1;
 }
 
-static unsigned char *cli_decodesig(const char *sig, unsigned int plen, fp_int e, fp_int n)
+static unsigned char *cli_decodesig(const char *sig, unsigned int plen, BIGNUM *e, BIGNUM *n)
 {
     int i, slen = strlen(sig), dec;
-    unsigned char *plain;
-    fp_int r, p, c;
+    unsigned char *plain = NULL, *ret_sig = NULL;
+    BIGNUM *r = NULL, *p = NULL, *c = NULL;
+    BN_CTX *bn_ctx;
+    unsigned int bn_bytes;
+    ;
 
-    fp_init(&r);
-    fp_init(&c);
+    r = BN_new();
+    if (!r) {
+        goto done;
+    }
+
+    p = BN_new();
+    if (!p) {
+        goto done;
+    }
+
+    c = BN_new();
+    if (!c) {
+        goto done;
+    }
+
+    bn_ctx = BN_CTX_new();
+    if (!bn_ctx) {
+        goto done;
+    }
+
+    BN_zero(c);
     for (i = 0; i < slen; i++) {
         if ((dec = cli_ndecode(sig[i])) < 0) {
-            return NULL;
+            goto done;
+        }
+        if (!BN_set_word(r, dec)) {
+            goto done;
+        }
+        if (!BN_lshift(r, r, 6 * i)) {
+            goto done;
         }
-        fp_set(&r, dec);
-        fp_mul_2d(&r, 6 * i, &r);
-        fp_add(&r, &c, &c);
-    }
 
-    plain = (unsigned char *)cli_calloc(plen + 1, sizeof(unsigned char));
+        if (!BN_add(c, c, r)) {
+            goto done;
+        }
+    }
+    if (!BN_mod_exp(p, c, e, n, bn_ctx)) {
+        goto done;
+    }
+    bn_bytes = BN_num_bytes(p);
+    /* Sometimes the size of the resulting BN (128) is larger than the expected
+     * length (16). The result does not match in this case. Instead of
+     * allocating memory and filling it, we fail early.
+     */
+    if (plen < bn_bytes) {
+        cli_errmsg("cli_decodesig: Resulting signature too large (%d vs %d).\n",
+                   bn_bytes, plen);
+        goto done;
+    }
+    plain = cli_calloc(plen, sizeof(unsigned char));
     if (!plain) {
         cli_errmsg("cli_decodesig: Can't allocate memory for 'plain'\n");
-        return NULL;
+        goto done;
     }
-    fp_init(&p);
-    fp_exptmod(&c, &e, &n, &p); /* plain = cipher^e mod n */
-    fp_set(&c, 256);
-    for (i = plen - 1; i >= 0; i--) { /* reverse */
-        fp_div(&p, &c, &p, &r);
-        plain[i] = MP_GET(&r);
+    if (!BN_bn2bin(p, plain)) {
+        goto done;
     }
 
-    return plain;
+    ret_sig = plain;
+    plain   = NULL;
+
+done:
+    BN_free(r);
+    BN_free(p);
+    BN_free(c);
+    BN_CTX_free(bn_ctx);
+    free(plain);
+    return ret_sig;
 }
 
 char *cli_getdsig(const char *host, const char *user, const unsigned char *data, unsigned int datalen, unsigned short mode)
@@ -228,41 +274,55 @@ char *cli_getdsig(const char *host, const char *user, const unsigned char *data,
     return strdup(pt);
 }
 
-int cli_versig(const char *md5, const char *dsig)
+cl_error_t cli_versig(const char *md5, const char *dsig)
 {
-    fp_int n, e;
-    char *pt, *pt2;
+    BIGNUM *n = NULL, *e = NULL;
+    char *pt = NULL, *pt2 = NULL;
+    int ret;
+
+    ret = CL_EMEM;
+    n   = BN_new();
+    if (!n)
+        goto done;
+
+    e = BN_new();
+    if (!e)
+        goto done;
+
+    ret = CL_EVERIFY;
+    if (!BN_dec2bn(&e, CLI_ESTR))
+        goto done;
+
+    if (!BN_dec2bn(&n, CLI_NSTR))
+        goto done;
 
     if (strlen(md5) != 32 || !isalnum(md5[0])) {
         /* someone is trying to fool us with empty/malformed MD5 ? */
         cli_errmsg("SECURITY WARNING: MD5 basic test failure.\n");
-        return CL_EVERIFY;
+        goto done;
     }
 
-    fp_init(&n);
-    fp_read_radix(&n, CLI_NSTR, 10);
-    fp_init(&e);
-    fp_read_radix(&e, CLI_ESTR, 10);
-
-    if (!(pt = (char *)cli_decodesig(dsig, 16, e, n))) {
-        return CL_EVERIFY;
-    }
+    if (!(pt = (char *)cli_decodesig(dsig, 16, e, n)))
+        goto done;
 
     pt2 = cli_str2hex(pt, 16);
-    free(pt);
 
     cli_dbgmsg("cli_versig: Decoded signature: %s\n", pt2);
 
     if (strncmp(md5, pt2, 32)) {
         cli_dbgmsg("cli_versig: Signature doesn't match.\n");
-        free(pt2);
-        return CL_EVERIFY;
+        goto done;
     }
 
-    free(pt2);
-
     cli_dbgmsg("cli_versig: Digital signature is correct.\n");
-    return CL_SUCCESS;
+    ret = CL_SUCCESS;
+
+done:
+    free(pt);
+    free(pt2);
+    BN_free(n);
+    BN_free(e);
+    return ret;
 }
 
 #define HASH_LEN 32
@@ -275,21 +335,39 @@ int cli_versig2(const unsigned char *sha256, const char *dsig_str, const char *n
     unsigned char mask[BLK_LEN], data[BLK_LEN], final[8 + 2 * HASH_LEN], c[4];
     unsigned int i, rounds;
     void *ctx;
-    fp_int n, e;
+    BIGNUM *n, *e;
+    int ret;
 
-    fp_init(&e);
-    fp_read_radix(&e, e_str, 10);
-    fp_init(&n);
-    fp_read_radix(&n, n_str, 10);
+    n = BN_new();
+    e = BN_new();
+
+    if (!n || !e) {
+        ret = CL_EMEM;
+        goto done;
+    }
+
+    ret = CL_EVERIFY;
+    if (!BN_dec2bn(&e, e_str))
+        goto done;
+
+    if (!BN_dec2bn(&n, n_str))
+        goto done;
 
     decoded = cli_decodesig(dsig_str, PAD_LEN, e, n);
-    if (!decoded)
-        return CL_EVERIFY;
+    if (!decoded) {
+        ret = CL_EVERIFY;
+        goto done;
+    }
 
     if (decoded[PAD_LEN - 1] != 0xbc) {
         free(decoded);
-        return CL_EVERIFY;
+        ret = CL_EVERIFY;
     }
+    BN_free(n);
+    BN_free(e);
+
+    n = NULL;
+    e = NULL;
 
     memcpy(mask, decoded, BLK_LEN);
     memcpy(digest2, &decoded[BLK_LEN], HASH_LEN);
@@ -337,4 +415,9 @@ int cli_versig2(const unsigned char *sha256, const char *dsig_str, const char *n
     cl_finish_hash(ctx, digest1);
 
     return memcmp(digest1, digest2, HASH_LEN) ? CL_EVERIFY : CL_SUCCESS;
+
+done:
+    BN_free(n);
+    BN_free(e);
+    return ret;
 }
diff --git a/libclamav/dsig.h b/libclamav/dsig.h
index cb11c65..36a3176 100644
--- a/libclamav/dsig.h
+++ b/libclamav/dsig.h
@@ -29,7 +29,7 @@
 #include "clamav-config.h"
 #endif
 
-int cli_versig(const char *md5, const char *dsig);
+cl_error_t cli_versig(const char *md5, const char *dsig);
 int cli_versig2(const unsigned char *sha256, const char *dsig_str, const char *n_str, const char *e_str);
 
 /**
diff --git a/libclamav/readdb.c b/libclamav/readdb.c
index 1af0584..4410892 100644
--- a/libclamav/readdb.c
+++ b/libclamav/readdb.c
@@ -3310,9 +3310,7 @@ static int cli_loadcrt(FILE *fs, struct cl_engine *engine, struct cli_dbio *dbio
     char *tokens[CRT_TOKENS + 1];
     size_t line = 0, tokens_count;
     cli_crt ca;
-    int ret             = CL_SUCCESS;
-    char *pubkey        = NULL;
-    const uint8_t exp[] = "\x01\x00\x01";
+    int ret = CL_SUCCESS;
 
     if (!(engine->dconf->pe & PE_CONF_CERTS)) {
         cli_dbgmsg("cli_loadcrt: Ignoring .crb sigs due to DCONF configuration\n");
@@ -3324,7 +3322,10 @@ static int cli_loadcrt(FILE *fs, struct cl_engine *engine, struct cli_dbio *dbio
         return ret;
     }
 
-    cli_crt_init(&ca);
+    if (cli_crt_init(&ca) < 0) {
+        cli_dbgmsg("cli_loadcrt: No mem for CA init.\n");
+        return CL_EMEM;
+    }
     memset(ca.issuer, 0xca, sizeof(ca.issuer));
 
     while (cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
@@ -3402,16 +3403,17 @@ static int cli_loadcrt(FILE *fs, struct cl_engine *engine, struct cli_dbio *dbio
             goto done;
         }
 
-        pubkey = cli_hex2str(tokens[4]);
-        if (!pubkey) {
+	if (BN_hex2bn(&ca.n, tokens[4]) == 0) {
             cli_errmsg("cli_loadcrt: line %u: Cannot convert public key to binary string\n", (unsigned int)line);
             ret = CL_EMALFDB;
             goto done;
         }
 
-        fp_read_unsigned_bin(&(ca.n), (const unsigned char *)pubkey, strlen(tokens[4]) / 2);
-
-        fp_read_unsigned_bin(&(ca.e), exp, sizeof(exp) - 1);
+	/* Set the RSA exponent of 65537 */
+	if (!BN_set_word(ca.e, 65537)) {
+		cli_errmsg("cli_loadcrt: Cannot set the exponent.\n");
+		goto done;
+	}
 
         switch (tokens[6][0]) {
             case '1':
@@ -3463,13 +3465,9 @@ static int cli_loadcrt(FILE *fs, struct cl_engine *engine, struct cli_dbio *dbio
 
         ca.hashtype = CLI_HASHTYPE_ANY;
         crtmgr_add(&(engine->cmgr), &ca);
-
-        FREE(pubkey);
     }
 
 done:
-    FREE(pubkey);
-
     cli_dbgmsg("Number of certs: %d\n", engine->cmgr.items);
     cli_crt_clear(&ca);
     return ret;
diff --git a/libclamav/textnorm.c b/libclamav/textnorm.c
index 6c45530..90db3e6 100644
--- a/libclamav/textnorm.c
+++ b/libclamav/textnorm.c
@@ -30,7 +30,6 @@
 #include <ctype.h>
 #include "clamav.h"
 #include "textnorm.h"
-#include "bignum.h"
 
 int text_normalize_init(struct text_norm_state *state, unsigned char *out, size_t out_len)
 {
diff --git a/libclamav/xdp.c b/libclamav/xdp.c
index f0e2fdd..97eeecf 100644
--- a/libclamav/xdp.c
+++ b/libclamav/xdp.c
@@ -52,7 +52,6 @@
 #include "scanners.h"
 #include "conv.h"
 #include "xdp.h"
-#include "bignum.h"
 #include "filetypes.h"
 
 static char *dump_xdp(cli_ctx *ctx, const char *start, size_t sz);
