Current section

Files

Jump to
argon2_elixir c_src argon2_nif.c
Raw

c_src/argon2_nif.c

/* Argon2 password hashing algorithm for use with Elixir
*
* Copyright 2016-2017 David Whitlock
*
* This is licensed under the Apache Public License 2.0
*
* The license and copyright information for the reference implementation
* is detailed below:
*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "argon2.h"
#include "encoding.h"
#include "core.h"
#include "erl_nif.h"
#define MAX_ENCODEDLEN 1024
ERL_NIF_TERM argon2_hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary pwd, salt;
unsigned int t_cost, m, m_cost, parallelism, raw_output, hashlen, encodedlen, version;
argon2_type type;
argon2_context context;
int result;
uint8_t *out;
if (argc != 10 || !enif_get_uint(env, argv[0], &t_cost) ||
!enif_get_uint(env, argv[1], &m) ||
!enif_get_uint(env, argv[2], &parallelism) ||
!enif_inspect_binary(env, argv[3], &pwd) ||
!enif_inspect_binary(env, argv[4], &salt) ||
!enif_get_uint(env, argv[5], &raw_output) ||
!enif_get_uint(env, argv[6], &hashlen) ||
!enif_get_uint(env, argv[7], &encodedlen) ||
!enif_get_uint(env, argv[8], &type) ||
!enif_get_uint(env, argv[9], &version))
return enif_make_badarg(env);
m_cost = (1<<m);
if (hashlen > ARGON2_MAX_OUTLEN) {
return enif_make_int(env, ARGON2_OUTPUT_TOO_LONG);
}
if (hashlen < ARGON2_MIN_OUTLEN) {
return enif_make_int(env, ARGON2_OUTPUT_TOO_SHORT);
}
out = malloc(hashlen);
if (!out) {
return enif_make_int(env, ARGON2_MEMORY_ALLOCATION_ERROR);
}
char *hash = malloc(hashlen * 2 + 1);
char *encoded = malloc(encodedlen);
context.out = (uint8_t *)out;
context.outlen = (uint32_t)hashlen;
context.pwd = CONST_CAST(uint8_t *)pwd.data;
context.pwdlen = (uint32_t)pwd.size;
context.salt = CONST_CAST(uint8_t *)salt.data;
context.saltlen = (uint32_t)salt.size;
context.secret = NULL;
context.secretlen = 0;
context.ad = NULL;
context.adlen = 0;
context.t_cost = t_cost;
context.m_cost = m_cost;
context.lanes = parallelism;
context.threads = parallelism;
context.allocate_cbk = NULL;
context.free_cbk = NULL;
context.flags = ARGON2_DEFAULT_FLAGS;
context.version = version ? version : ARGON2_VERSION_NUMBER;
result = argon2_ctx(&context, type);
if (result != ARGON2_OK) {
clear_internal_memory(out, hashlen);
free(out);
free(hash);
free(encoded);
return enif_make_int(env, result);
}
/* if raw hash requested, write it */
if (raw_output) {
size_t i;
for (i = 0; i < hashlen; i++)
sprintf(hash + i * 2, "%02x", out[i]);
} else {
hash[0] = '\0';
}
/* if encoding requested, write it */
if (encodedlen) {
if (encode_string(encoded, encodedlen, &context, type) != ARGON2_OK) {
clear_internal_memory(out, hashlen); /* wipe buffers if error */
clear_internal_memory(encoded, encodedlen);
free(out);
free(hash);
free(encoded);
return enif_make_int(env, ARGON2_ENCODING_FAIL);
}
} else {
encoded[0] = '\0';
}
secure_wipe_memory(out, hashlen);
free(out);
ERL_NIF_TERM result_term = enif_make_tuple2(env, enif_make_string(env, hash, ERL_NIF_LATIN1),
enif_make_string(env, encoded, ERL_NIF_LATIN1));
free(hash);
free(encoded);
return result_term;
}
static ERL_NIF_TERM argon2_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
char encoded[MAX_ENCODEDLEN];
ErlNifBinary pwd;
argon2_type type;
int ret;
if (argc != 3 || !enif_get_string(env, argv[0], encoded, sizeof(encoded), ERL_NIF_LATIN1) ||
!enif_inspect_binary(env, argv[1], &pwd) ||
!enif_get_uint(env, argv[2], &type))
return enif_make_badarg(env);
ret = argon2_verify(encoded, pwd.data, pwd.size, type);
return enif_make_int(env, ret);
}
static ERL_NIF_TERM argon2_error_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int error_code;
char const *ret;
if (argc != 1 || !enif_get_int(env, argv[0], &error_code))
return enif_make_badarg(env);
ret = argon2_error_message(error_code);
return enif_make_string(env, ret, ERL_NIF_LATIN1);
}
static ERL_NIF_TERM argon2_encodedlen_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
unsigned int t_cost, m, m_cost, parallelism, saltlen, hashlen;
argon2_type type;
int ret;
if (argc != 6 || !enif_get_uint(env, argv[0], &t_cost) ||
!enif_get_uint(env, argv[1], &m) ||
!enif_get_uint(env, argv[2], &parallelism) ||
!enif_get_uint(env, argv[3], &saltlen) ||
!enif_get_uint(env, argv[4], &hashlen) ||
!enif_get_uint(env, argv[5], &type))
return enif_make_badarg(env);
m_cost = (1<<m);
ret = argon2_encodedlen(t_cost, m_cost, parallelism, saltlen, hashlen, type);
return enif_make_int(env, ret);
}
static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info)
{
return 0;
}
static ErlNifFunc argon2_nif_funcs[] =
{
{"hash_nif", 10, argon2_hash_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"verify_nif", 3, argon2_verify_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"error_nif", 1, argon2_error_nif},
{"encodedlen_nif", 6, argon2_encodedlen_nif}
};
ERL_NIF_INIT(Elixir.Argon2.Base, argon2_nif_funcs, NULL, NULL, upgrade, NULL)