aboutsummaryrefslogblamecommitdiffstats
path: root/lib/crypto/c_src/hash.c
blob: 9b79258585da7d70abade669f4fc79642ab2ad0c (plain) (tree)



















                                                                           


                   








                                                   

                                                                 



                    
                                                           
                                            
 
                                                                     




                                  


      





                                                                                     

                                 


             
 
                                                                 


                                                                     
      

 

























                                                                               






                                                                          
                               
 

                      






                                                            


                                                        





                                                                         



                                                  





                                 






                                                                               
                                     

                             

                      










                                                                                         
 
                                       








                                 

                                   




                                                                                 
                                              


                      

                      



                                                                         
 







                                                                                             

                                           
                            








                                 

                                       








                                                                                
                            
 

                      

                                                                         



                                                        








                                                                   
                                                            



                                 
 





                                 











                                                                               
                               
 

                      



                                                  


                                    
               



                                         

               



                                         

                     



                                               
      




























                                            
                 



                     





                                                                   
                                               





                                 













                                                                                 

                      












                                                            


                                    
               



                                               

               



                                               

                     



                                                     
      




























                                                  
                 



                       

                             
 

                                                                           
                                         


                                                        


                                                    





                                 










                                                                                
                                         

                                       
                               
 

                      










                                                      


                            
               



                                            

               



                                            
      
                     
                      


                                                  
      




























                                               
                 



                      




                                                 
 
                                        
 
















                                                                                  



                                          
/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 2010-2018. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * %CopyrightEnd%
 */

#include "hash.h"
#include "digest.h"

#ifdef HAVE_MD5
#  define MD5_CTX_LEN       (sizeof(MD5_CTX))
#endif
#ifdef HAVE_MD4
#  define MD4_CTX_LEN       (sizeof(MD4_CTX))
#endif
#ifdef HAVE_RIPEMD160
#  define RIPEMD160_CTX_LEN (sizeof(RIPEMD160_CTX))
#endif

#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0)
struct evp_md_ctx {
    EVP_MD_CTX* ctx;
};

/* Define resource types for OpenSSL context structures. */
static ErlNifResourceType* evp_md_ctx_rtype;

static void evp_md_ctx_dtor(ErlNifEnv* env, struct evp_md_ctx *ctx) {
    if (ctx == NULL)
        return;

    if (ctx->ctx)
        EVP_MD_CTX_free(ctx->ctx);
}
#endif

int init_hash_ctx(ErlNifEnv* env) {
#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0)
    evp_md_ctx_rtype = enif_open_resource_type(env, NULL, "EVP_MD_CTX",
                                               (ErlNifResourceDtor*) evp_md_ctx_dtor,
                                               ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER,
                                               NULL);
    if (evp_md_ctx_rtype == NULL)
        goto err;
#endif

    return 1;

#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0)
 err:
    PRINTF_ERR0("CRYPTO: Could not open resource type 'EVP_MD_CTX'");
    return 0;
#endif
}

ERL_NIF_TERM hash_info_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Type) */
    struct digest_type_t *digp = NULL;
    const EVP_MD         *md;
    ERL_NIF_TERM         ret;

    ASSERT(argc == 1);

    if ((digp = get_digest_type(argv[0])) == NULL)
        return enif_make_badarg(env);

    if ((md = digp->md.p) == NULL)
        return atom_notsup;

    ret = enif_make_new_map(env);

    enif_make_map_put(env, ret, atom_type,
        enif_make_int(env, EVP_MD_type(md)), &ret);
    enif_make_map_put(env, ret, atom_size,
        enif_make_int(env, EVP_MD_size(md)), &ret);
    enif_make_map_put(env, ret, atom_block_size,
        enif_make_int(env, EVP_MD_block_size(md)), &ret);

    return ret;
}

ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Type, Data) */
    struct digest_type_t *digp = NULL;
    const EVP_MD         *md;
    ErlNifBinary         data;
    ERL_NIF_TERM         ret;
    unsigned             ret_size;
    unsigned char        *outp;

    ASSERT(argc == 2);

    if ((digp = get_digest_type(argv[0])) == NULL)
        goto bad_arg;
    if (!enif_inspect_iolist_as_binary(env, argv[1], &data))
        goto bad_arg;

    if ((md = digp->md.p) == NULL)
        goto err;

    ret_size = (unsigned)EVP_MD_size(md);
    ASSERT(0 < ret_size && ret_size <= EVP_MAX_MD_SIZE);

    if ((outp = enif_make_new_binary(env, ret_size, &ret)) == NULL)
        goto err;
    if (EVP_Digest(data.data, data.size, outp, &ret_size, md, NULL) != 1)
        goto err;

    ASSERT(ret_size == (unsigned)EVP_MD_size(md));

    CONSUME_REDS(env, data);
    return ret;

 bad_arg:
    return enif_make_badarg(env);

 err:
    return atom_notsup;
}

#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0)

ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Type) */
    struct digest_type_t *digp = NULL;
    struct evp_md_ctx    *ctx = NULL;
    ERL_NIF_TERM         ret;

    ASSERT(argc == 1);

    if ((digp = get_digest_type(argv[0])) == NULL)
        goto bad_arg;
    if (digp->md.p == NULL)
        goto err;

    if ((ctx = enif_alloc_resource(evp_md_ctx_rtype, sizeof(struct evp_md_ctx))) == NULL)
        goto err;
    if ((ctx->ctx = EVP_MD_CTX_new()) == NULL)
        goto err;
    if (EVP_DigestInit(ctx->ctx, digp->md.p) != 1)
        goto err;

    ret = enif_make_resource(env, ctx);
    goto done;

 bad_arg:
    return enif_make_badarg(env);

 err:
    ret = atom_notsup;

 done:
    if (ctx)
        enif_release_resource(ctx);
    return ret;
}

ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Context, Data) */
    struct evp_md_ctx   *ctx, *new_ctx = NULL;
    ErlNifBinary data;
    ERL_NIF_TERM ret;

    ASSERT(argc == 2);

    if (!enif_get_resource(env, argv[0], evp_md_ctx_rtype, (void**)&ctx))
        goto bad_arg;
    if (!enif_inspect_iolist_as_binary(env, argv[1], &data))
        goto bad_arg;

    if ((new_ctx = enif_alloc_resource(evp_md_ctx_rtype, sizeof(struct evp_md_ctx))) == NULL)
        goto err;
    if ((new_ctx->ctx = EVP_MD_CTX_new()) == NULL)
        goto err;
    if (EVP_MD_CTX_copy(new_ctx->ctx, ctx->ctx) != 1)
        goto err;
    if (EVP_DigestUpdate(new_ctx->ctx, data.data, data.size) != 1)
        goto err;

    ret = enif_make_resource(env, new_ctx);
    CONSUME_REDS(env, data);
    goto done;

 bad_arg:
    return enif_make_badarg(env);

 err:
    ret = atom_notsup;

 done:
    if (new_ctx)
        enif_release_resource(new_ctx);
    return ret;
}

ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Context) */
    struct evp_md_ctx *ctx;
    EVP_MD_CTX        *new_ctx;
    ERL_NIF_TERM  ret;
    unsigned      ret_size;
    unsigned char     *outp;

    ASSERT(argc == 1);

    if (!enif_get_resource(env, argv[0], evp_md_ctx_rtype, (void**)&ctx))
        goto bad_arg;

    ret_size = (unsigned)EVP_MD_CTX_size(ctx->ctx);
    ASSERT(0 < ret_size && ret_size <= EVP_MAX_MD_SIZE);

    if ((new_ctx = EVP_MD_CTX_new()) == NULL)
        goto err;
    if (EVP_MD_CTX_copy(new_ctx, ctx->ctx) != 1)
        goto err;
    if ((outp = enif_make_new_binary(env, ret_size, &ret)) == NULL)
        goto err;
    if (EVP_DigestFinal(new_ctx, outp, &ret_size) != 1)
        goto err;

    ASSERT(ret_size == (unsigned)EVP_MD_CTX_size(ctx->ctx));
    goto done;

 bad_arg:
    return enif_make_badarg(env);

 err:
    ret = atom_notsup;

 done:
    if (new_ctx)
        EVP_MD_CTX_free(new_ctx);
    return ret;
}

#else /* if OPENSSL_VERSION_NUMBER < 1.0 */

ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Type) */
    typedef int (*init_fun)(unsigned char*);
    struct digest_type_t *digp = NULL;
    ERL_NIF_TERM         ctx;
    size_t               ctx_size = 0;
    init_fun             ctx_init = 0;
    unsigned char        *outp;

    ASSERT(argc == 1);

    if ((digp = get_digest_type(argv[0])) == NULL)
        goto bad_arg;
    if (digp->md.p == NULL)
        goto err;

    switch (EVP_MD_type(digp->md.p))
    {
#ifdef HAVE_MD4
    case NID_md4:
        ctx_size = MD4_CTX_LEN;
        ctx_init = (init_fun)(&MD4_Init);
        break;
#endif
#ifdef HAVE_MD5
    case NID_md5:
        ctx_size = MD5_CTX_LEN;
        ctx_init = (init_fun)(&MD5_Init);
        break;
#endif
#ifdef HAVE_RIPEMD160
    case NID_ripemd160:
        ctx_size = RIPEMD160_CTX_LEN;
        ctx_init = (init_fun)(&RIPEMD160_Init);
        break;
#endif
    case NID_sha1:
        ctx_size = sizeof(SHA_CTX);
        ctx_init = (init_fun)(&SHA1_Init);
        break;
#ifdef HAVE_SHA224
    case NID_sha224:
        ctx_size = sizeof(SHA256_CTX);
        ctx_init = (init_fun)(&SHA224_Init);
        break;
#endif
#ifdef HAVE_SHA256
    case NID_sha256:
        ctx_size = sizeof(SHA256_CTX);
        ctx_init = (init_fun)(&SHA256_Init);
        break;
#endif
#ifdef HAVE_SHA384
    case NID_sha384:
        ctx_size = sizeof(SHA512_CTX);
        ctx_init = (init_fun)(&SHA384_Init);
        break;
#endif
#ifdef HAVE_SHA512
    case NID_sha512:
        ctx_size = sizeof(SHA512_CTX);
        ctx_init = (init_fun)(&SHA512_Init);
        break;
#endif
    default:
        goto err;
    }
    ASSERT(ctx_size);
    ASSERT(ctx_init);

    if ((outp = enif_make_new_binary(env, ctx_size, &ctx)) == NULL)
        goto err;

    if (ctx_init(outp) != 1)
        goto err;

    return enif_make_tuple2(env, argv[0], ctx);

 bad_arg:
    return enif_make_badarg(env);

 err:
    return atom_notsup;
}

ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* ({Type, Context}, Data) */
    typedef int (*update_fun)(unsigned char*, const unsigned char*, size_t);
    ERL_NIF_TERM         new_ctx;
    ErlNifBinary         ctx, data;
    const ERL_NIF_TERM   *tuple;
    int                  arity;
    struct digest_type_t *digp = NULL;
    unsigned char        *ctx_buff;
    size_t               ctx_size   = 0;
    update_fun           ctx_update = 0;

    ASSERT(argc == 2);

    if (!enif_get_tuple(env, argv[0], &arity, &tuple))
        goto bad_arg;
    if (arity != 2)
        goto bad_arg;
    if ((digp = get_digest_type(tuple[0])) == NULL)
        goto bad_arg;
    if (!enif_inspect_binary(env, tuple[1], &ctx))
        goto bad_arg;
    if (!enif_inspect_iolist_as_binary(env, argv[1], &data))
        goto bad_arg;

    if (digp->md.p == NULL)
        goto err;

    switch (EVP_MD_type(digp->md.p))
    {
#ifdef HAVE_MD4
    case NID_md4:
        ctx_size   = MD4_CTX_LEN;
        ctx_update = (update_fun)(&MD4_Update);
        break;
#endif
#ifdef HAVE_MD5
    case NID_md5:
        ctx_size   = MD5_CTX_LEN;
        ctx_update = (update_fun)(&MD5_Update);
        break;
#endif
#ifdef HAVE_RIPEMD160
    case NID_ripemd160:
        ctx_size   = RIPEMD160_CTX_LEN;
        ctx_update = (update_fun)(&RIPEMD160_Update);
        break;
#endif
    case NID_sha1:
        ctx_size   = sizeof(SHA_CTX);
        ctx_update = (update_fun)(&SHA1_Update);
        break;
#ifdef HAVE_SHA224
    case NID_sha224:
        ctx_size   = sizeof(SHA256_CTX);
        ctx_update = (update_fun)(&SHA224_Update);
        break;
#endif
#ifdef HAVE_SHA256
    case NID_sha256:
        ctx_size   = sizeof(SHA256_CTX);
        ctx_update = (update_fun)(&SHA256_Update);
        break;
#endif
#ifdef HAVE_SHA384
    case NID_sha384:
        ctx_size   = sizeof(SHA512_CTX);
        ctx_update = (update_fun)(&SHA384_Update);
        break;
#endif
#ifdef HAVE_SHA512
    case NID_sha512:
        ctx_size   = sizeof(SHA512_CTX);
        ctx_update = (update_fun)(&SHA512_Update);
        break;
#endif
    default:
        goto err;
    }
    ASSERT(ctx_size);
    ASSERT(ctx_update);

    if (ctx.size != ctx_size)
        goto bad_arg;

    if ((ctx_buff = enif_make_new_binary(env, ctx_size, &new_ctx)) == NULL)
        goto err;
    memcpy(ctx_buff, ctx.data, ctx_size);

    if (ctx_update(ctx_buff, data.data, data.size) != 1)
        goto err;

    CONSUME_REDS(env, data);
    return enif_make_tuple2(env, tuple[0], new_ctx);

 bad_arg:
    return enif_make_badarg(env);

 err:
    return atom_notsup;
}

ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* ({Type, Context}) */
    typedef int (*final_fun)(unsigned char*, void*);
    ERL_NIF_TERM         ret;
    ErlNifBinary         ctx;
    const ERL_NIF_TERM   *tuple;
    int                  arity;
    struct digest_type_t *digp = NULL;
    const EVP_MD         *md;
    void                 *new_ctx = NULL;
    size_t               ctx_size  = 0;
    final_fun            ctx_final = 0;
    unsigned char        *outp;

    ASSERT(argc == 1);

    if (!enif_get_tuple(env, argv[0], &arity, &tuple))
        goto bad_arg;
    if (arity != 2)
        goto bad_arg;
    if ((digp = get_digest_type(tuple[0])) == NULL)
        goto bad_arg;
    if (!enif_inspect_binary(env, tuple[1], &ctx))
        goto bad_arg;

    if ((md = digp->md.p) == NULL)
        goto err;

    switch (EVP_MD_type(md))
    {
#ifdef HAVE_MD4
    case NID_md4:
        ctx_size  = MD4_CTX_LEN;
        ctx_final = (final_fun)(&MD4_Final);
        break;
#endif
#ifdef HAVE_MD5
    case NID_md5:
        ctx_size  = MD5_CTX_LEN;
        ctx_final = (final_fun)(&MD5_Final);
        break;
#endif
#ifdef HAVE_RIPEMD160
   case NID_ripemd160:
        ctx_size  = RIPEMD160_CTX_LEN;
        ctx_final = (final_fun)(&RIPEMD160_Final);
        break;
#endif
    case NID_sha1:
        ctx_size  = sizeof(SHA_CTX);
        ctx_final = (final_fun)(&SHA1_Final);
        break;
#ifdef HAVE_SHA224
    case NID_sha224:
        ctx_size  = sizeof(SHA256_CTX);
        ctx_final = (final_fun)(&SHA224_Final);
        break;
#endif
#ifdef HAVE_SHA256
    case NID_sha256:
        ctx_size  = sizeof(SHA256_CTX);
        ctx_final = (final_fun)(&SHA256_Final);
        break;
#endif
#ifdef HAVE_SHA384
    case NID_sha384:
        ctx_size  = sizeof(SHA512_CTX);
        ctx_final = (final_fun)(&SHA384_Final);
        break;
#endif
#ifdef HAVE_SHA512
    case NID_sha512:
        ctx_size  = sizeof(SHA512_CTX);
        ctx_final = (final_fun)(&SHA512_Final);
        break;
#endif
    default:
        goto err;
    }
    ASSERT(ctx_size);
    ASSERT(ctx_final);

    if (ctx.size != ctx_size)
        goto bad_arg;

    if ((new_ctx = enif_alloc(ctx_size)) == NULL)
        goto err;

    memcpy(new_ctx, ctx.data, ctx_size);

    if ((outp = enif_make_new_binary(env, (size_t)EVP_MD_size(md), &ret)) == NULL)
        goto err;

    if (ctx_final(outp, new_ctx) != 1)
        goto err;

    goto done;

 bad_arg:
    return enif_make_badarg(env);

 err:
    ret = atom_notsup;

 done:
    if (new_ctx)
        enif_free(new_ctx);
    return ret;
}

#endif  /* OPENSSL_VERSION_NUMBER < 1.0 */