/*
* %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"
#define MD5_CTX_LEN (sizeof(MD5_CTX))
#define MD4_CTX_LEN (sizeof(MD4_CTX))
#define RIPEMD160_CTX_LEN (sizeof(RIPEMD160_CTX))
#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) {
PRINTF_ERR0("CRYPTO: Could not open resource type 'EVP_MD_CTX'");
return 0;
}
#endif
return 1;
}
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;
if (argc != 2)
goto bad_arg;
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;
if (argc != 1)
goto bad_arg;
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:
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;
if (argc != 2)
goto bad_arg;
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:
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;
if (argc != 1)
goto bad_arg;
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;
if (argc != 1)
goto bad_arg;
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))
{
case NID_md4:
ctx_size = MD4_CTX_LEN;
ctx_init = (init_fun)(&MD4_Init);
break;
case NID_md5:
ctx_size = MD5_CTX_LEN;
ctx_init = (init_fun)(&MD5_Init);
break;
case NID_ripemd160:
ctx_size = RIPEMD160_CTX_LEN;
ctx_init = (init_fun)(&RIPEMD160_Init);
break;
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;
if (argc != 2)
goto bad_arg;
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))
{
case NID_md4:
ctx_size = MD4_CTX_LEN;
ctx_update = (update_fun)(&MD4_Update);
break;
case NID_md5:
ctx_size = MD5_CTX_LEN;
ctx_update = (update_fun)(&MD5_Update);
break;
case NID_ripemd160:
ctx_size = RIPEMD160_CTX_LEN;
ctx_update = (update_fun)(&RIPEMD160_Update);
break;
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;
if (argc != 1)
goto bad_arg;
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))
{
case NID_md4:
ctx_size = MD4_CTX_LEN;
ctx_final = (final_fun)(&MD4_Final);
break;
case NID_md5:
ctx_size = MD5_CTX_LEN;
ctx_final = (final_fun)(&MD5_Final);
break;
case NID_ripemd160:
ctx_size = RIPEMD160_CTX_LEN;
ctx_final = (final_fun)(&RIPEMD160_Final);
break;
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 */