I am developing an ENGINE for OpenSSL 1.0.0a and 0.9.8o. Among other things 
this engine implements digest methods like SHA1.

While testing this engine I discovered memory leaks when using 
PEM_write_bio_PKCS8PrivateKey().
I traced the leak back to the inner and outer SHA1 digest contexts used for 
HMAC in PKCS5_PBKDF2_HMAC().
The leak is related to the md_data in the EVP_MD_CTX.  My engine saves it's own 
data in ctx->md_data which includes allocation of additional pointers.
That data gets initialized by my ENGINE implementation of the init() callback 
and destroyed by the cleanup() callback.
One thing I have noticed about the ENGINE callback design is that the init() 
callback cannot check the md_data to see if it was previously already 
initialized.
OpenSSL doesn't initialize the memory to 0 when md_data is allocated. So my 
init() callback has no choice but to assume that the md_data is uninitialized.
So if the init() callback is called more than once without calling the 
cleanup() callback my ENGINE implementation will introduce a memory leak.
This is where the source of the problem lies.

In the code below the "while(tkeylen)" loop may result in multiple passes of 
the HMAC code. On each pass it does HMAC_Init_ex(),HMAC_Update(),HMAC_Final().
HMAC_CTX_cleanup() is only called once after exiting the while loop.

Each time HMAC_Init_ex() is called my ENGINE SHA1 init() callback is called and 
it initializes the md_data for the inner and outer digest contexts.
But HMAC_Final() never calls EVP_DigestFinal_ex() or EVP_MD_CTX_cleanup() on 
these inner and outer digest contexts.
Because these inner/outer digest context are not finalized or cleaned up, each 
pass through the loop results in them being re-initialized by my init()
callback and memory leaks introduced.

If I modify HMAC_Final() to call EVP_MD_CTX_cleanup(&ctx->i_ctx) and 
EVP_MD_CTX_cleanup(&ctx->o_ctx), then my memory leaks go away.
So either there is a bug in OpenSSL or I am misunderstanding the ENGINE digest 
interfaces.



int PKCS5_PBKDF2_HMAC(const char *pass, int passlen,
                           const unsigned char *salt, int saltlen, int iter,
                           const EVP_MD *digest,
                           int keylen, unsigned char *out)
        {
        unsigned char digtmp[EVP_MAX_MD_SIZE], *p, itmp[4];
        int cplen, j, k, tkeylen, mdlen;
        unsigned long i = 1;
        HMAC_CTX hctx;

        mdlen = EVP_MD_size(digest);
        if (mdlen < 0)
                return 0;

        HMAC_CTX_init(&hctx);
        p = out;
        tkeylen = keylen;
        if(!pass)
                passlen = 0;
        else if(passlen == -1)
                passlen = strlen(pass);
        while(tkeylen)
                {
                if(tkeylen > mdlen)
                        cplen = mdlen;
                else
                        cplen = tkeylen;
                /* We are unlikely to ever use more than 256 blocks (5120 bits!)
                 * but just in case...
                 */
                itmp[0] = (unsigned char)((i >> 24) & 0xff);
                itmp[1] = (unsigned char)((i >> 16) & 0xff);
                itmp[2] = (unsigned char)((i >> 8) & 0xff);
                itmp[3] = (unsigned char)(i & 0xff);
                HMAC_Init_ex(&hctx, pass, passlen, digest, NULL);       
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< source of memory leaks is from 2nd and later 
passes here
                HMAC_Update(&hctx, salt, saltlen);
                HMAC_Update(&hctx, itmp, 4);
                HMAC_Final(&hctx, digtmp, NULL);
                memcpy(p, digtmp, cplen);
                for(j = 1; j < iter; j++)
                        {
                        HMAC(digest, pass, passlen,
                                 digtmp, mdlen, digtmp, NULL);
                        for(k = 0; k < cplen; k++)
                                p[k] ^= digtmp[k];
                        }
                tkeylen-= cplen;
                i++;
                p+= cplen;
                }
        HMAC_CTX_cleanup(&hctx);
#ifdef DEBUG_PKCS5V2
        fprintf(stderr, "Password:\n");
        h__dump (pass, passlen);
        fprintf(stderr, "Salt:\n");
        h__dump (salt, saltlen);
        fprintf(stderr, "Iteration count %d\n", iter);
        fprintf(stderr, "Key:\n");
        h__dump (out, keylen);
#endif
        return 1;
        }


int HMAC_Final(HMAC_CTX *ctx, unsigned char *md, unsigned int *len)
        {
        int j;
        unsigned int i;
        unsigned char buf[EVP_MAX_MD_SIZE];

        j=EVP_MD_block_size(ctx->md);

        if (!EVP_DigestFinal_ex(&ctx->md_ctx,buf,&i))
                goto err;
        if (!EVP_MD_CTX_copy_ex(&ctx->md_ctx,&ctx->o_ctx))
                goto err;
        if (!EVP_DigestUpdate(&ctx->md_ctx,buf,i))
                goto err;
        if (!EVP_DigestFinal_ex(&ctx->md_ctx,md,len))
                goto err;

>>>>>>>>>>>>> THE INNER (ctx->i_ctx ) AND OUTER (ctx->o_ctx ) DIGEST CONTEXTS 
>>>>>>>>>>>>> ARE NOT CLEANED UP OR FINALIZED HERE.

        return 1;
        err:
        return 0;


        }

--
Robert Dugal            Senior Software Developer
Certicom Corp.          A Subsidiary of Research In Motion
[email protected]
direct                  905.501.3848
fax                             905.507.4230
www.certicom.com



---------------------------------------------------------------------
This transmission (including any attachments) may contain confidential 
information, privileged material (including material protected by the 
solicitor-client or other applicable privileges), or constitute non-public 
information. Any use of this information by anyone other than the intended 
recipient is prohibited. If you have received this transmission in error, 
please immediately reply to the sender and delete this information from your 
system. Use, dissemination, distribution, or reproduction of this transmission 
by unintended recipients is not authorized and may be unlawful.
______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
Development Mailing List                       [email protected]
Automated List Manager                           [email protected]

Reply via email to