diff options
Diffstat (limited to 'server/_build/default/lib/bcrypt/c_src/bcrypt_nif.c')
-rw-r--r-- | server/_build/default/lib/bcrypt/c_src/bcrypt_nif.c | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/server/_build/default/lib/bcrypt/c_src/bcrypt_nif.c b/server/_build/default/lib/bcrypt/c_src/bcrypt_nif.c new file mode 100644 index 0000000..fd3492a --- /dev/null +++ b/server/_build/default/lib/bcrypt/c_src/bcrypt_nif.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2011-2012 Hunter Morris <hunter.morris@smarkets.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "erl_nif.h" +#include "erl_blf.h" +#include "bcrypt_nif.h" + +static void free_task(task_t* task) +{ + if (task->env != NULL) + enif_free_env(task->env); + enif_free(task); +} + +static task_t* alloc_task(task_type_t type) +{ + task_t* task = (task_t*)enif_alloc(sizeof(task_t)); + if (task == NULL) + return NULL; + (void)memset(task, 0, sizeof(task_t)); + task->type = type; + return task; +} + +static task_t* alloc_init_task(task_type_t type, ERL_NIF_TERM ref, ErlNifPid pid, int num_orig_terms, const ERL_NIF_TERM orig_terms[]) +{ + task_t* task = alloc_task(type); + task->pid = pid; + task->env = enif_alloc_env(); + if (task->env == NULL) { + free_task(task); + return NULL; + } + + if (type == HASH) { + assert(num_orig_terms == 2); + if (!enif_inspect_iolist_as_binary( + task->env, enif_make_copy(task->env, orig_terms[0]), + &task->data.hash.salt)) { + free_task(task); + return NULL; + } + if (!enif_inspect_iolist_as_binary( + task->env, enif_make_copy(task->env, orig_terms[1]), + &task->data.hash.password)) { + free_task(task); + return NULL; + } + } + + task->ref = enif_make_copy(task->env, ref); + return task; +} + +static ERL_NIF_TERM hashpw(task_t* task) +{ + char password[1024] = { 0 }; + char salt[1024] = { 0 }; + char encrypted[1024] = { 0 }; + + size_t password_sz = 1024; + if (password_sz > task->data.hash.password.size) + password_sz = task->data.hash.password.size; + (void)memcpy(&password, task->data.hash.password.data, password_sz); + + size_t salt_sz = 1024; + if (salt_sz > task->data.hash.salt.size) + salt_sz = task->data.hash.salt.size; + (void)memcpy(&salt, task->data.hash.salt.data, salt_sz); + + if (ts_bcrypt(encrypted, password, salt)) { + return enif_make_tuple3( + task->env, + enif_make_atom(task->env, "error"), + task->ref, + enif_make_string(task->env, "bcrypt failed", ERL_NIF_LATIN1)); + } + + return enif_make_tuple3( + task->env, + enif_make_atom(task->env, "ok"), + task->ref, + enif_make_string(task->env, encrypted, ERL_NIF_LATIN1)); +} + +static void* async_worker(void* arg) +{ + ctx_t* ctx; + task_t* task; + + ERL_NIF_TERM result; + + ctx = (ctx_t*)arg; + + while (1) { + task = (task_t*)async_queue_pop(ctx->queue); + + if (task->type == SHUTDOWN) { + free_task(task); + break; + } else if (task->type == HASH) { + result = hashpw(task); + } else { + errx(1, "Unexpected task type: %i", task->type); + } + + enif_send(NULL, &task->pid, task->env, result); + free_task(task); + } + + return NULL; +} + +static ERL_NIF_TERM bcrypt_encode_salt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary csalt, bin; + unsigned long log_rounds; + ERL_NIF_TERM ret; + + if (!enif_inspect_binary(env, argv[0], &csalt) || 16 != csalt.size) { + return enif_make_badarg(env); + } + + if (!enif_get_ulong(env, argv[1], &log_rounds)) { + enif_release_binary(&csalt); + return enif_make_badarg(env); + } + + if (!enif_alloc_binary(64, &bin)) { + enif_release_binary(&csalt); + return enif_make_badarg(env); + } + + encode_salt((char *)bin.data, (u_int8_t*)csalt.data, csalt.size, log_rounds); + enif_release_binary(&csalt); + + ret = enif_make_string(env, (char *)bin.data, ERL_NIF_LATIN1); + enif_release_binary(&bin); + return ret; +} + +static ERL_NIF_TERM bcrypt_hashpw(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ctx_t *ctx; + task_t *task; + ErlNifPid pid; + + if (argc != 5) + return enif_make_badarg(env); + + bcrypt_privdata_t *priv = (bcrypt_privdata_t*)enif_priv_data(env); + + if (!enif_get_resource(env, argv[0], priv->bcrypt_rt, (void**)(&ctx))) + return enif_make_badarg(env); + + if (!enif_is_ref(env, argv[1])) + return enif_make_badarg(env); + + if (!enif_get_local_pid(env, argv[2], &pid)) + return enif_make_badarg(env); + + ERL_NIF_TERM orig_terms[] = { argv[4], argv[3] }; + task = alloc_init_task(HASH, argv[1], pid, 2, orig_terms); + + if (!task) + return enif_make_badarg(env); + + async_queue_push(ctx->queue, task); + + return enif_make_atom(env, "ok"); +} + +static ERL_NIF_TERM bcrypt_create_ctx(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM ret; + bcrypt_privdata_t *priv = (bcrypt_privdata_t*)enif_priv_data(env); + ctx_t* ctx = (ctx_t*)enif_alloc_resource(priv->bcrypt_rt, sizeof(ctx_t)); + if (ctx == NULL) + return enif_make_badarg(env); + ctx->queue = async_queue_create("bcrypt_queue_mutex", "bcrypt_queue_condvar"); + ctx->topts = enif_thread_opts_create("bcrypt_thread_opts"); + if (enif_thread_create("bcrypt_worker", &ctx->tid, async_worker, ctx, ctx->topts) != 0) { + enif_release_resource(ctx); + return enif_make_badarg(env); + } + ret = enif_make_resource(env, ctx); + enif_release_resource(ctx); + return ret; +} + +static ErlNifFunc bcrypt_nif_funcs[] = +{ + {"encode_salt", 2, bcrypt_encode_salt}, + {"hashpw", 5, bcrypt_hashpw}, + {"create_ctx", 0, bcrypt_create_ctx}, +}; + +static void bcrypt_rt_dtor(ErlNifEnv* env, void* obj) +{ + ctx_t *ctx = (ctx_t*)obj; + task_t *task = alloc_task(SHUTDOWN); + void *result = NULL; + + async_queue_push(ctx->queue, task); + enif_thread_join(ctx->tid, &result); + async_queue_destroy(ctx->queue); + enif_thread_opts_destroy(ctx->topts); +} + +static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + const char *mod = "bcrypt_nif"; + const char *name = "nif_resource"; + + ErlNifResourceFlags flags = (ErlNifResourceFlags)(ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER); + + bcrypt_privdata_t *priv = (bcrypt_privdata_t*)enif_alloc(sizeof(bcrypt_privdata_t)); + priv->bcrypt_rt = enif_open_resource_type(env, mod, name, bcrypt_rt_dtor, flags, NULL); + if (priv->bcrypt_rt == NULL) + return -1; + *priv_data = priv; + return 0; +} + +ERL_NIF_INIT(bcrypt_nif, bcrypt_nif_funcs, &on_load, NULL, NULL, NULL) |