initial commit

main
Aydin Mercan 2023-05-31 14:31:03 +03:00
commit 2982515f2e
Signed by: jaiden
SSH Key Fingerprint: SHA256:vy6hjzotbn/MWZAbjzURNk3NL62EPkjoHsJ5xr/s7nk
30 changed files with 1824 additions and 0 deletions

4
.formatter.exs Normal file
View File

@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

28
.gitignore vendored Normal file
View File

@ -0,0 +1,28 @@
# The directory Mix will write compiled artifacts to.
/_build/
# If you run "mix test --cover", coverage assets end up here.
/cover/
# The directory Mix downloads your dependencies sources to.
/deps/
# Where third-party dependencies like ExDoc output generated docs.
/doc/
# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch
# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez
# Ignore package tarball (built via "mix hex.build").
saltywitch-*.tar
# Temporary files, for example, from tests.
/tmp/
*.o

47
CMakeLists.txt Normal file
View File

@ -0,0 +1,47 @@
cmake_minimum_required(VERSION 3.26)
project(saltywitch)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
endif()
set(CMAKE_C_STANDARD 99)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_LINK_WHAT_YOU_USE ON)
if(CMAKE_C_COMPILER_FRONTEND_VARIANT STREQUAL "GNU")
set(CMAKE_C_FLAGS "-O3 -shared ${CMAKE_C_FLAGS}")
endif()
check_ipo_supported(RESULT has_ipo OUTPUT error)
if(has_ipo)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
endif()
set(SALTYWITCH_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/c_src/saltywitch.c
${CMAKE_CURRENT_SOURCE_DIR}/c_src/atoms.c
${CMAKE_CURRENT_SOURCE_DIR}/c_src/auth.c
${CMAKE_CURRENT_SOURCE_DIR}/c_src/generichash.c
${CMAKE_CURRENT_SOURCE_DIR}/c_src/pwhash.c
${CMAKE_CURRENT_SOURCE_DIR}/c_src/saltywitch.c
${CMAKE_CURRENT_SOURCE_DIR}/c_src/saltywitch.h
${CMAKE_CURRENT_SOURCE_DIR}/c_src/secretbox.c
${CMAKE_CURRENT_SOURCE_DIR}/c_src/shorthash.c
)
set(INCLUDE_DIRS
${ERTS_INCLUDE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/c_src
)
find_package(libsodium REQUIRED)
add_library(saltywitch SHARED ${SALTYWITCH_SOURCES})
target_compile_definitions(saltywitch PUBLIC "$<$<CONFIG:RELEASE>:NDEBUG>")
target_include_directories(${PROJECT_NAME} PUBLIC ${INCLUDE_DIRS})
target_link_libraries(saltywitch PRIVATE libsodium)

7
Makefile Normal file
View File

@ -0,0 +1,7 @@
.POSIX:
build:
busybox sh ./build.sh
clean:
sh ./clean.sh

41
README.md Normal file
View File

@ -0,0 +1,41 @@
# SaltyWitch
Another NIF library for libsodium.
## Roadmap
Easy cross-compiling the NIF.
## Building
On UNIX-like systems:
- Shell (tested on bash and busybox ash)
- Make
- A GCC-like C99 compiler.
- `meson` _(optional but **recommended**)_
- `pkgconf` *(if directly building)*
`SALTYWITCH_BUILD_SYSTEM`: By default `direct` is used for UNIX systems.
- **meson** _(recommended)_: Use meson to build the NIF.
- **cmake**: Use CMake to build the NIF
- **direct**: Build the NIF in a single compiler invocation like a lunatic.
`SALTYWITCH_MESON_ARGS`: extra arguments for meson
- do not specify `--prefix`
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `saltywitch` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:saltywitch, "~> 0.0.1"}
]
end
```
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at <https://hexdocs.pm/saltywitch>.

17
STYLE.md Normal file
View File

@ -0,0 +1,17 @@
# C Style used
- Macros are to be avoided.
- Any macro defined must be defined before its used and `undef`'d after done.
Exception part must be as following at bare minimum:
If there are more than 1 points of exception, they all must be deduplicated through `goto`'s.
The variables `err` and `reason` must be used with the following initial values.
```c
ERL_NIF_TERM err = atom_badarg;
ERL_NIF_TERM reason = atom_err_opaque;
```

59
build.sh Executable file
View File

@ -0,0 +1,59 @@
#!/usr/bin/env sh
if [ -z $SALTYWITCH_BUILD_SYSTEM ]; then
printf "[build.sh] trying to detect better build systems... "
# if [ -x "$(command -v cmake)" ]; then
# printf "cmake "
# SALTYWITCH_BUILD_SYSTEM="cmake"
# fi
if [ -x "$(command -v meson)" ]; then
printf "meson "
SALTYWITCH_BUILD_SYSTEM="meson"
fi
printf "\n"
fi
case ${SALTYWITCH_BUILD_SYSTEM:-direct} in
meson)
echo "[build.sh] using meson to build nif"
if ! [ -d "./build-meson" ]; then
meson setup build-meson \
--prefix=$MIX_APP_PATH \
-Dmix_target=$MIX_TARGET \
-Derts_include_dir=$ERTS_INCLUDE_DIR \
-Derl_interface_lib_dir=$ERL_INTERFACE_LIB_DIR \
-Derl_interface_include_dir=$ERL_INTERFACE_INCLUDE_DIR \
$SALTYWITCH_MESON_ARGS
fi
meson compile -C build-meson
meson install -C build-meson
;;
# cmake)
# echo "[build.sh] using cmake to build nif"
# ;;
direct)
echo "[build.sh] directly building the nif"
${CC:-cc} c_src/*.c \
-shared \
${CFLAGS:--O3 -fPIC -fstack-protector -fvisibility=hidden} \
$LDFLAGS \
-I $ERTS_INCLUDE_DIR \
-I $ERL_EI_INCLUDE_DIR \
-I $ERL_INTERFACE_INCLUDE_DIR \
-L $ERL_EI_LIBDIR \
-lei \
$(pkgconf --libs --cflags libsodium) \
-o $MIX_APP_PATH/priv/saltywitch.so
;;
*)
echo "[build.sh] invalid build backend"
exit 1
;;
esac

66
c_src/atoms.c Normal file
View File

@ -0,0 +1,66 @@
#include "saltywitch.h"
ERL_NIF_TERM atom_badarg;
ERL_NIF_TERM atom_error;
ERL_NIF_TERM atom_ok;
ERL_NIF_TERM atom_err_opaque;
ERL_NIF_TERM atom_err_invalid_type;
ERL_NIF_TERM atom_err_nif_alloc;
ERL_NIF_TERM atom_err_invalid_key_size;
ERL_NIF_TERM atom_err_invalid_nonce_size;
ERL_NIF_TERM atom_err_invalid_salt_size;
ERL_NIF_TERM atom_err_invalid_seed_size;
ERL_NIF_TERM atom_err_invalid_tag_size;
ERL_NIF_TERM atom_err_verification_failed;
ERL_NIF_TERM atom_err_ciphertext_too_small;
ERL_NIF_TERM atom_err_key_too_large;
ERL_NIF_TERM atom_err_key_too_small;
ERL_NIF_TERM atom_err_output_too_large;
ERL_NIF_TERM atom_err_output_too_small;
ERL_NIF_TERM atom_err_pwhash_too_long;
ERL_NIF_TERM atom_err_pwhash_needs_rehash;
ERL_NIF_TERM atom_err_pwhash_mem_too_large;
ERL_NIF_TERM atom_err_pwhash_mem_too_small;
ERL_NIF_TERM atom_err_pwhash_ops_too_large;
ERL_NIF_TERM atom_err_pwhash_ops_too_small;
#define DEFERROR(name) atom_err_##name = enif_make_atom(env, #name)
void saltywitch_nif_init_atoms(ErlNifEnv *env)
{
atom_error = enif_make_atom(env, "error");
atom_badarg = enif_make_atom(env, "badarg");
atom_ok = enif_make_atom(env, "ok");
DEFERROR(opaque);
DEFERROR(invalid_type);
DEFERROR(nif_alloc);
DEFERROR(invalid_key_size);
DEFERROR(invalid_nonce_size);
DEFERROR(invalid_salt_size);
DEFERROR(invalid_seed_size);
DEFERROR(invalid_tag_size);
DEFERROR(verification_failed);
DEFERROR(ciphertext_too_small);
DEFERROR(key_too_large);
DEFERROR(key_too_small);
DEFERROR(output_too_large);
DEFERROR(output_too_small);
DEFERROR(pwhash_too_long);
DEFERROR(pwhash_needs_rehash);
DEFERROR(pwhash_mem_too_large);
DEFERROR(pwhash_mem_too_small);
DEFERROR(pwhash_ops_too_large);
DEFERROR(pwhash_ops_too_small);
}
#undef DEFERROR

102
c_src/auth.c Normal file
View File

@ -0,0 +1,102 @@
#include "saltywitch.h"
/* auth */
ERL_NIF_TERM saltywitch_auth_keygen(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM term;
unsigned char *key = enif_make_new_binary(env, crypto_auth_KEYBYTES, &term);
if (key == NULL) {
return saltywitch_exception(env, atom_error, atom_err_nif_alloc);
}
crypto_auth_keygen(key);
return term;
}
ERL_NIF_TERM saltywitch_auth(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary message, key;
ERL_NIF_TERM term;
unsigned char *mac;
ERL_NIF_TERM err = atom_badarg;
ERL_NIF_TERM reason = atom_err_opaque;
if (enif_inspect_binary(env, argv[0], &message) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (enif_inspect_binary(env, argv[1], &key) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (key.size != crypto_auth_KEYBYTES) {
reason = atom_err_invalid_key_size;
goto badarg_FAULT;
}
mac = enif_make_new_binary(env, crypto_auth_BYTES, &term);
if (mac == NULL) {
reason = atom_err_nif_alloc;
goto error_FAULT;
}
crypto_auth(mac, message.data, message.size, key.data);
return term;
error_FAULT:
err = atom_error;
badarg_FAULT:
return saltywitch_exception(env, err, reason);
}
ERL_NIF_TERM saltywitch_auth_verify(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary tag, message, key;
ERL_NIF_TERM err = atom_badarg;
ERL_NIF_TERM reason = atom_err_opaque;
if (enif_inspect_binary(env, argv[0], &tag) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (tag.size != crypto_auth_BYTES) {
reason = atom_err_invalid_tag_size;
goto badarg_FAULT;
}
if (enif_inspect_binary(env, argv[1], &message) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (enif_inspect_binary(env, argv[2], &key) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (key.size != crypto_auth_KEYBYTES) {
reason = atom_err_invalid_key_size;
goto badarg_FAULT;
}
if (crypto_auth_verify(tag.data, message.data, message.size, key.data) != 0) {
reason = atom_err_verification_failed;
goto error_FAULT;
}
return atom_ok;
error_FAULT:
err = atom_error;
badarg_FAULT:
return saltywitch_exception(env, err, reason);
}

262
c_src/generichash.c Normal file
View File

@ -0,0 +1,262 @@
#include "saltywitch.h"
struct saltywitch_generichash_context {
uint64_t outlen;
ErlNifMutex *mutex;
crypto_generichash_state *state;
};
static ErlNifResourceType *_generichash_context_rtype = NULL;
static void _generichash_dtor(ErlNifEnv *env, void *context)
{
struct saltywitch_generichash_context *ctx = context;
if (ctx == NULL) {
return;
}
if (ctx->state != NULL) {
sodium_free(ctx->state);
ctx->state = NULL;
}
if (ctx->mutex != NULL) {
enif_mutex_destroy(ctx->mutex);
ctx->mutex = NULL;
}
}
int saltywitch_nif_init_generichash(ErlNifEnv *env)
{
_generichash_context_rtype = enif_open_resource_type(env,
NULL,
"generichash_context",
_generichash_dtor,
ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER,
NULL);
if (_generichash_context_rtype == NULL) {
return -1;
}
return 0;
}
ERL_NIF_TERM saltywitch_generichash_keygen(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM term;
unsigned char *key = enif_make_new_binary(env, crypto_generichash_KEYBYTES, &term);
if (key == NULL) {
return saltywitch_exception(env, atom_error, atom_err_nif_alloc);
}
crypto_generichash_keygen(key);
return term;
}
ERL_NIF_TERM saltywitch_generichash(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
unsigned char *hash = NULL;
ERL_NIF_TERM term;
ErlNifBinary bin;
ErlNifBinary key = {
.size = 0,
.data = NULL,
};
uint64_t len = crypto_generichash_BYTES;
if (enif_inspect_binary(env, argv[0], &bin) == false) {
return enif_make_badarg(env);
}
switch (argc) {
case 3:
if (enif_get_uint64(env, argv[2], &len) == false) {
return enif_make_badarg(env);
}
/* fallthrough */
case 2:
if (enif_inspect_binary(env, argv[1], &key) == false) {
return enif_make_badarg(env);
}
}
hash = enif_make_new_binary(env, len, &term);
if (hash == NULL) {
return enif_raise_exception(env, enif_make_atom(env, "opaque"));
}
crypto_generichash(hash, len, bin.data, bin.size, key.data, key.size);
return term;
}
ERL_NIF_TERM saltywitch_generichash_init(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
struct saltywitch_generichash_context *ctx;
ERL_NIF_TERM term;
ERL_NIF_TERM err = atom_badarg;
ERL_NIF_TERM reason = atom_err_opaque;
ErlNifBinary key = {
.size = 0,
.data = NULL,
};
uint64_t len = crypto_generichash_BYTES;
switch (argc) {
case 2:
if (enif_get_uint64(env, argv[1], &len) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (len < crypto_generichash_BYTES_MIN) {
reason = atom_err_output_too_small;
goto badarg_FAULT;
}
if (len > crypto_generichash_BYTES_MAX) {
reason = atom_err_output_too_large;
goto badarg_FAULT;
}
/* fallthrough */
case 1:
if (enif_inspect_binary(env, argv[0], &key) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (key.size < crypto_generichash_KEYBYTES_MIN) {
reason = atom_err_key_too_small;
goto badarg_FAULT;
}
if (key.size > crypto_generichash_KEYBYTES_MAX) {
reason = atom_err_key_too_large;
goto badarg_FAULT;
}
}
ctx = enif_alloc_resource(_generichash_context_rtype, sizeof(struct saltywitch_generichash_context));
if (ctx == NULL) {
reason = atom_err_nif_alloc;
goto resource_alloc_FAULT;
}
ctx->outlen = len;
ctx->mutex = enif_mutex_create("saltywitch.generichash");
if (ctx->mutex == NULL) {
reason = atom_err_nif_alloc;
goto mutex_FAULT;
}
ctx->state = sodium_malloc(sizeof(crypto_generichash_state));
if (ctx->state == NULL) {
goto state_alloc_FAULT;
}
crypto_generichash_init(ctx->state, key.data, key.size, len);
term = enif_make_resource(env, ctx);
enif_release_resource(ctx);
return term;
state_alloc_FAULT:
enif_mutex_destroy(ctx->mutex);
mutex_FAULT:
enif_release_resource(ctx);
resource_alloc_FAULT:
err = atom_error;
badarg_FAULT:
return saltywitch_exception(env, err, reason);
}
ERL_NIF_TERM saltywitch_generichash_update(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
struct saltywitch_generichash_context *ctx = NULL;
ErlNifBinary message;
ERL_NIF_TERM err = atom_badarg;
ERL_NIF_TERM reason = atom_err_opaque;
if (enif_get_resource(env, argv[0], _generichash_context_rtype, (void **) &ctx) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (ctx == NULL) {
goto badarg_FAULT;
}
if (ctx->mutex == NULL || ctx->state == NULL) {
goto badarg_FAULT;
}
if (enif_inspect_iolist_as_binary(env, argv[1], &message) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
enif_mutex_lock(ctx->mutex);
crypto_generichash_update(ctx->state, message.data, message.size);
enif_mutex_unlock(ctx->mutex);
return atom_ok;
badarg_FAULT:
return saltywitch_exception(env, err, reason);
}
ERL_NIF_TERM saltywitch_generichash_final(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
struct saltywitch_generichash_context *ctx = NULL;
unsigned char *hash;
ERL_NIF_TERM term;
ERL_NIF_TERM err = atom_badarg;
ERL_NIF_TERM reason = atom_err_opaque;
if (enif_get_resource(env, argv[0], _generichash_context_rtype, (void **) &ctx) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (ctx == NULL) {
goto badarg_FAULT;
}
if (ctx->mutex == NULL || ctx->state == NULL) {
goto badarg_FAULT;
}
hash = enif_make_new_binary(env, ctx->outlen, &term);
if (hash == NULL) {
reason = atom_err_nif_alloc;
goto error_FAULT;
}
enif_mutex_lock(ctx->mutex);
crypto_generichash_final(ctx->state, hash, ctx->outlen);
sodium_free(ctx->state);
ctx->state = NULL;
ctx->outlen = 0;
enif_mutex_unlock(ctx->mutex);
return term;
error_FAULT:
err = atom_error;
badarg_FAULT:
return saltywitch_exception(env, err, reason);
}

1
c_src/kx.c Normal file
View File

@ -0,0 +1 @@
#include "saltywitch.h"

199
c_src/pwhash.c Normal file
View File

@ -0,0 +1,199 @@
#include "saltywitch.h"
ERL_NIF_TERM saltywitch_pwhash(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary pass, salt;
ERL_NIF_TERM term;
uint64_t len, ops, mem;
unsigned char *out;
int alg;
ERL_NIF_TERM err = atom_badarg;
ERL_NIF_TERM reason = atom_err_opaque;
if (enif_get_uint64(env, argv[0], &len) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (enif_inspect_iolist_as_binary(env, argv[1], &pass) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (enif_inspect_binary(env, argv[2], &salt) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (salt.size != crypto_pwhash_SALTBYTES) {
reason = atom_err_invalid_salt_size;
goto badarg_FAULT;
}
if (enif_get_uint64(env, argv[3], &ops) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (ops > crypto_pwhash_OPSLIMIT_MAX) {
reason = atom_err_pwhash_ops_too_large;
goto badarg_FAULT;
} else if (ops < crypto_pwhash_OPSLIMIT_MIN) {
reason = atom_err_pwhash_ops_too_small;
goto badarg_FAULT;
}
if (enif_get_uint64(env, argv[4], &mem) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (mem > crypto_pwhash_OPSLIMIT_MAX) {
reason = atom_err_pwhash_mem_too_large;
goto badarg_FAULT;
} else if (mem < crypto_pwhash_OPSLIMIT_MIN) {
reason = atom_err_pwhash_mem_too_small;
goto badarg_FAULT;
}
if (enif_get_int(env, argv[5], &alg) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
out = enif_make_new_binary(env, len, &term);
if (out == NULL) {
reason = atom_err_nif_alloc;
goto error_FAULT;
}
if (crypto_pwhash(out, len, (char *) pass.data, pass.size, salt.data, ops, mem, alg) != 0) {
goto error_FAULT;
}
return term;
error_FAULT:
err = atom_error;
badarg_FAULT:
return saltywitch_exception(env, err, reason);
}
ERL_NIF_TERM saltywitch_pwhash_str(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
char out[crypto_pwhash_STRBYTES];
uint64_t ops, mem;
ErlNifBinary pass;
ERL_NIF_TERM err = atom_badarg;
ERL_NIF_TERM reason = atom_err_opaque;
(void) argc;
if (enif_inspect_iolist_as_binary(env, argv[0], &pass) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (enif_get_uint64(env, argv[1], &ops) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (enif_get_uint64(env, argv[2], &mem) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (crypto_pwhash_str(out, (char *) pass.data, pass.size, ops, mem) != 0) {
goto error_FAULT;
}
return enif_make_string(env, out, ERL_NIF_LATIN1);
error_FAULT:
err = atom_error;
badarg_FAULT:
return saltywitch_exception(env, err, reason);
}
#include <stdio.h>
ERL_NIF_TERM saltywitch_pwhash_str_verify(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary hash, pass;
ERL_NIF_TERM err = atom_badarg;
ERL_NIF_TERM reason = atom_err_opaque;
(void) argc;
if (enif_inspect_iolist_as_binary(env, argv[0], &hash) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (hash.size > crypto_pwhash_STRBYTES) {
reason = atom_err_pwhash_too_long;
goto badarg_FAULT;
}
if (enif_inspect_iolist_as_binary(env, argv[1], &pass) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (crypto_pwhash_str_verify((char *) hash.data, (char *) pass.data, pass.size) != 0) {
reason = atom_err_verification_failed;
goto error_FAULT;
}
return atom_ok;
error_FAULT:
err = atom_error;
badarg_FAULT:
return saltywitch_exception(env, err, reason);
}
ERL_NIF_TERM saltywitch_pwhash_str_needs_rehash(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary hash;
uint64_t ops, mem;
ERL_NIF_TERM err = atom_badarg;
ERL_NIF_TERM reason = atom_err_opaque;
if (enif_inspect_binary(env, argv[0], &hash) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (hash.size > crypto_pwhash_STRBYTES) {
reason = atom_err_pwhash_too_long;
goto badarg_FAULT;
}
if (enif_get_uint64(env, argv[1], &ops) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (enif_get_uint64(env, argv[2], &mem) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
switch (crypto_pwhash_str_needs_rehash((char *) hash.data, ops, mem)) {
case 0:
return atom_ok;
case 1:
reason = atom_err_pwhash_needs_rehash;
}
err = atom_error;
badarg_FAULT:
return saltywitch_exception(env, err, reason);
}

97
c_src/random.c Normal file
View File

@ -0,0 +1,97 @@
#include "saltywitch.h"
_Static_assert(sizeof(unsigned int) >= sizeof(uint32_t), "unsigned int must be greated than 32 bits");
ERL_NIF_TERM saltywitch_randombytes_random(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
(void) argc;
(void) argv;
return enif_make_uint(env, randombytes_random());
}
ERL_NIF_TERM saltywitch_randombytes_uniform(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
uint32_t upper;
(void) argc;
if (enif_get_uint(env, argv[0], &upper) == false) {
return saltywitch_exception(env, atom_badarg, atom_err_invalid_type);
}
return enif_make_uint(env, randombytes_uniform(upper));
}
ERL_NIF_TERM saltywitch_randombytes_buf(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
unsigned char *buf;
ERL_NIF_TERM term;
uint64_t len;
(void) argc;
if (enif_get_uint64(env, argv[0], &len) == false) {
return saltywitch_exception(env, atom_badarg, atom_err_invalid_type);
}
buf = enif_make_new_binary(env, len, &term);
if (buf == NULL) {
return saltywitch_exception(env, atom_error, atom_err_nif_alloc);
}
randombytes_buf(buf, len);
return term;
}
ERL_NIF_TERM saltywitch_randombytes_buf_deterministic(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
unsigned char *buf;
ErlNifBinary seed;
ERL_NIF_TERM term;
uint64_t len;
ERL_NIF_TERM err = atom_badarg;
ERL_NIF_TERM reason = atom_err_opaque;
(void) argc;
if (enif_get_uint64(env, argv[0], &len) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (enif_inspect_binary(env, argv[1], &seed) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (seed.size != randombytes_SEEDBYTES) {
reason = atom_err_invalid_seed_size;
goto badarg_FAULT;
}
buf = enif_make_new_binary(env, len, &term);
if (buf == NULL) {
reason = atom_err_nif_alloc;
goto error_FAULT;
}
randombytes_buf_deterministic(buf, len, seed.data);
return term;
error_FAULT:
err = atom_error;
badarg_FAULT:
return saltywitch_exception(env, err, reason);
}
ERL_NIF_TERM saltywitch_randombytes_seedbytes(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
(void) argc;
(void) argv;
return enif_make_uint64(env, randombytes_SEEDBYTES);
}

93
c_src/saltywitch.c Normal file
View File

@ -0,0 +1,93 @@
#include "saltywitch.h"
_Static_assert((sizeof(size_t) >= sizeof(uint64_t)), "size_t must be at least 64-bits");
ERL_NIF_TERM saltywitch_exception(ErlNifEnv *env, ERL_NIF_TERM type, ERL_NIF_TERM reason)
{
ERL_NIF_TERM exception = enif_make_tuple2(env, type, reason);
return enif_raise_exception(env, exception);
}
static int saltywitch_load(ErlNifEnv *env, void **data, ERL_NIF_TERM info)
{
(void) data;
(void) info;
if (sodium_init() < 0) {
return -1;
}
saltywitch_nif_init_atoms(env);
if (saltywitch_nif_init_generichash(env) != 0) {
return -1;
}
return 0;
}
static ERL_NIF_TERM saltywitch_info(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
(void) argc;
(void) argv;
return enif_make_tuple3(env,
enif_make_string(env, SODIUM_VERSION_STRING, ERL_NIF_LATIN1),
enif_make_int(env, SODIUM_LIBRARY_VERSION_MAJOR),
enif_make_int(env, SODIUM_LIBRARY_VERSION_MINOR));
}
static ErlNifFunc nif_funcs[] = {
{"info", 0, saltywitch_info},
/* random data */
{"randombytes_random", 0, saltywitch_randombytes_random},
{"randombytes_uniform", 1, saltywitch_randombytes_uniform},
{"randombytes_buf", 1, saltywitch_randombytes_buf},
{"randombytes_buf_deterministic", 2, saltywitch_randombytes_buf_deterministic},
{"randombytes_seedbytes", 0, saltywitch_randombytes_seedbytes},
/* secretstream */
/*
{"secretstream_xchacha20poly1305_keygen", 0, saltywitch_secretstream_xchacha20poly1305_keygen},
{"secretstream_xchacha20poly1305_init_push", 1, saltywitch_secretstream_xchacha20poly1305_init_push},
*/
/* secretbox */
{"secretbox_keygen", 0, saltywitch_secretbox_keygen},
{"secretbox_easy", 3, saltywitch_secretbox_easy},
{"secretbox_open_easy", 3, saltywitch_secretbox_open_easy},
{"secretbox_detached", 3, saltywitch_secretbox_detached},
{"secretbox_open_detached", 4, saltywitch_secretbox_open_detached},
{"secretbox_keybytes", 0, saltywitch_secretbox_keybytes},
{"secretbox_macbytes", 0, saltywitch_secretbox_macbytes},
{"secretbox_noncebytes", 0, saltywitch_secretbox_noncebytes},
/* generichash */
{"generichash", 1, saltywitch_generichash},
{"generichash", 2, saltywitch_generichash},
{"generichash_keygen", 0, saltywitch_generichash_keygen},
{"generichash_init", 0, saltywitch_generichash_init},
{"generichash_init", 1, saltywitch_generichash_init},
{"generichash_init", 2, saltywitch_generichash_init},
{"generichash_update", 2, saltywitch_generichash_update, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"generichash_final", 1, saltywitch_generichash_final},
/* shorthash */
{"shorthash_keygen", 0, saltywitch_shorthash_keygen},
{"shorthash", 2, saltywitch_shorthash},
/* pwhash */
//{"pwhash", 6, saltywitch_pwhash, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"pwhash_str", 3, saltywitch_pwhash_str, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"pwhash_str_verify", 2, saltywitch_pwhash_str_verify, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"pwhash_str_needs_rehash", 3, saltywitch_pwhash_str_needs_rehash},
};
ERL_NIF_INIT(Elixir.SaltyWitch.NIF, nif_funcs, saltywitch_load, NULL, NULL, NULL)

113
c_src/saltywitch.h Normal file
View File

@ -0,0 +1,113 @@
#pragma once
#include <erl_nif.h>
#include <sodium.h>
#include <stdint.h>
#if __STDC_VERSION__ < 202300L
#include <stdbool.h>
#endif
extern ERL_NIF_TERM atom_badarg;
extern ERL_NIF_TERM atom_error;
extern ERL_NIF_TERM atom_ok;
extern ERL_NIF_TERM atom_err_opaque;
extern ERL_NIF_TERM atom_err_invalid_type;
extern ERL_NIF_TERM atom_err_nif_alloc;
extern ERL_NIF_TERM atom_err_invalid_key_size;
extern ERL_NIF_TERM atom_err_invalid_nonce_size;
extern ERL_NIF_TERM atom_err_invalid_salt_size;
extern ERL_NIF_TERM atom_err_invalid_seed_size;
extern ERL_NIF_TERM atom_err_invalid_tag_size;
extern ERL_NIF_TERM atom_err_verification_failed;
extern ERL_NIF_TERM atom_err_ciphertext_too_small;
extern ERL_NIF_TERM atom_err_key_too_large;
extern ERL_NIF_TERM atom_err_key_too_small;
extern ERL_NIF_TERM atom_err_output_too_large;
extern ERL_NIF_TERM atom_err_output_too_small;
extern ERL_NIF_TERM atom_err_pwhash_too_long;
extern ERL_NIF_TERM atom_err_pwhash_needs_rehash;
extern ERL_NIF_TERM atom_err_pwhash_mem_too_large;
extern ERL_NIF_TERM atom_err_pwhash_mem_too_small;
extern ERL_NIF_TERM atom_err_pwhash_ops_too_large;
extern ERL_NIF_TERM atom_err_pwhash_ops_too_small;
/* init */
void saltywitch_nif_init_atoms(ErlNifEnv *env);
int saltywitch_nif_init_generichash(ErlNifEnv *env);
/* helpers */
ERL_NIF_TERM saltywitch_exception(ErlNifEnv *env, ERL_NIF_TERM type, ERL_NIF_TERM reason);
/* random */
ERL_NIF_TERM saltywitch_randombytes_random(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_randombytes_uniform(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_randombytes_buf(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_randombytes_buf_deterministic(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_randombytes_seedbytes(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
/* generichash */
ERL_NIF_TERM saltywitch_generichash(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_generichash_keygen(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_generichash_init(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_generichash_update(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_generichash_final(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
/* secretbox*/
ERL_NIF_TERM saltywitch_secretbox_keygen(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_secretbox_easy(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_secretbox_open_easy(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_secretbox_detached(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_secretbox_open_detached(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_secretbox_keybytes(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_secretbox_macbytes(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_secretbox_noncebytes(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
/* auth */
ERL_NIF_TERM saltywitch_auth_keygen(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_auth(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_auth_verify(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_auth_hmacsha256_keygen(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_auth_hmacsha256(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_auth_hmacsha256_verify(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_auth_hmacsha512_keygen(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_auth_hmacsha512(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_auth_hmacsha512_verify(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_auth_hmacsha512256_keygen(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_auth_hmacsha512256(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_auth_hmacsha512256_verify(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
/* pwhash */
ERL_NIF_TERM saltywitch_pwhash(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_pwhash_str(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_pwhash_str_verify(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_pwhash_str_needs_rehash(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
/* shorthash */
ERL_NIF_TERM saltywitch_shorthash_keygen(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_shorthash(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
/* key exchange */
/*
ERL_NIF_TERM saltywitch_kx_keypair(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_kx_seed_keypair(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_kx_client_session_keys(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_kx_server_session_keys(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_kx_publickeybytes(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM saltywitch_kx_publickeybytes(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
*/

260
c_src/secretbox.c Normal file
View File

@ -0,0 +1,260 @@
#include "saltywitch.h"
ERL_NIF_TERM saltywitch_secretbox_keygen(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM term;
unsigned char *key = enif_make_new_binary(env, crypto_secretbox_KEYBYTES, &term);
if (key == NULL) {
return saltywitch_exception(env, atom_error, atom_err_nif_alloc);
}
crypto_secretbox_keygen(key);
return term;
}
ERL_NIF_TERM saltywitch_secretbox_easy(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary key, nonce, message;
unsigned char *cipher;
ERL_NIF_TERM term;
ERL_NIF_TERM err = atom_badarg;
ERL_NIF_TERM reason = atom_err_opaque;
if (enif_inspect_binary(env, argv[0], &key) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (key.size != crypto_secretbox_KEYBYTES) {
reason = atom_err_invalid_key_size;
goto badarg_FAULT;
}
if (enif_inspect_binary(env, argv[1], &nonce) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (nonce.size != crypto_secretbox_NONCEBYTES) {
reason = atom_err_invalid_nonce_size;
goto badarg_FAULT;
}
if (enif_inspect_binary(env, argv[2], &message) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
cipher = enif_make_new_binary(env, message.size + crypto_secretbox_MACBYTES, &term);
if (cipher == NULL) {
reason = atom_err_nif_alloc;
goto error_FAULT;
}
crypto_secretbox_easy(cipher, message.data, message.size, nonce.data, key.data);
return term;
error_FAULT:
err = atom_error;
badarg_FAULT:
return saltywitch_exception(env, err, reason);
}
ERL_NIF_TERM saltywitch_secretbox_open_easy(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary key, nonce, cipher;
unsigned char *message;
ERL_NIF_TERM term;
ERL_NIF_TERM err = atom_badarg;
ERL_NIF_TERM reason = atom_err_opaque;
(void) argc;
if (enif_inspect_binary(env, argv[0], &key) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (key.size != crypto_secretbox_KEYBYTES) {
reason = atom_err_invalid_key_size;
goto badarg_FAULT;
}
if (enif_inspect_binary(env, argv[1], &nonce) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (nonce.size != crypto_secretbox_NONCEBYTES) {
reason = atom_err_invalid_nonce_size;
goto badarg_FAULT;
}
if (enif_inspect_binary(env, argv[2], &cipher) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (cipher.size <= crypto_secretbox_MACBYTES) {
reason = atom_err_ciphertext_too_small;
goto badarg_FAULT;
}
message = enif_make_new_binary(env, cipher.size - crypto_secretbox_MACBYTES, &term);
if (message == NULL) {
reason = atom_err_nif_alloc;
goto error_FAULT;
}
if (crypto_secretbox_open_easy(message, cipher.data, cipher.size, nonce.data, key.data) != 0) {
reason = atom_err_invalid_type;
goto error_FAULT;
}
return term;
error_FAULT:
err = atom_error;
badarg_FAULT:
return saltywitch_exception(env, err, reason);
}
ERL_NIF_TERM saltywitch_secretbox_detached(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM cipher_term, mac_term;
ErlNifBinary key, message, nonce;
unsigned char *cipher, *mac;
ERL_NIF_TERM err = atom_badarg;
ERL_NIF_TERM reason = atom_err_opaque;
if (enif_inspect_binary(env, argv[0], &key) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (key.size != crypto_secretbox_KEYBYTES) {
reason = atom_err_invalid_key_size;
goto badarg_FAULT;
}
if (enif_inspect_binary(env, argv[1], &nonce) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (nonce.size != crypto_secretbox_NONCEBYTES) {
reason = atom_err_invalid_nonce_size;
goto badarg_FAULT;
}
if (enif_inspect_binary(env, argv[2], &message) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
cipher = enif_make_new_binary(env, message.size, &cipher_term);
if (cipher == NULL) {
reason = atom_err_nif_alloc;
goto error_FAULT;
}
mac = enif_make_new_binary(env, crypto_secretbox_MACBYTES, &mac_term);
if (mac == NULL) {
reason = atom_err_nif_alloc;
goto error_FAULT;
}
crypto_secretbox_detached(cipher, mac, message.data, message.size, nonce.data, key.data);
return enif_make_tuple2(env, cipher_term, mac_term);
error_FAULT:
err = atom_error;
badarg_FAULT:
return saltywitch_exception(env, err, reason);
}
ERL_NIF_TERM saltywitch_secretbox_open_detached(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary key, nonce, cipher, mac;
unsigned char *message;
ERL_NIF_TERM term;
ERL_NIF_TERM err = atom_badarg;
ERL_NIF_TERM reason = atom_err_opaque;
if (enif_inspect_binary(env, argv[0], &key) == false) {
reason = atom_err_invalid_type;
goto FAULT;
}
if (key.size != crypto_secretbox_KEYBYTES) {
goto FAULT;
}
if (enif_inspect_binary(env, argv[1], &nonce) == false) {
reason = atom_err_invalid_type;
goto FAULT;
}
if (nonce.size != crypto_secretbox_NONCEBYTES) {
goto FAULT;
}
if (enif_inspect_binary(env, argv[2], &cipher) == false) {
reason = atom_err_invalid_type;
goto FAULT;
}
if (enif_inspect_binary(env, argv[3], &mac) == false) {
reason = atom_err_invalid_type;
goto FAULT;
}
if (mac.size != crypto_secretbox_MACBYTES) {
goto FAULT;
}
message = enif_make_new_binary(env, cipher.size, &term);
if (crypto_secretbox_open_detached(message, cipher.data, mac.data, cipher.size, nonce.data, key.data) != 0) {
err = atom_error;
goto FAULT;
}
return term;
FAULT:
term = saltywitch_exception(env, err, reason);
return term;
}
ERL_NIF_TERM saltywitch_secretbox_keybytes(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
(void) argc;
(void) argv;
return enif_make_uint(env, crypto_secretbox_KEYBYTES);
}
ERL_NIF_TERM saltywitch_secretbox_macbytes(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
(void) argc;
(void) argv;
return enif_make_uint(env, crypto_secretbox_MACBYTES);
}
ERL_NIF_TERM saltywitch_secretbox_noncebytes(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
(void) argc;
(void) argv;
return enif_make_uint(env, crypto_secretbox_NONCEBYTES);
}

60
c_src/shorthash.c Normal file
View File

@ -0,0 +1,60 @@
#include "saltywitch.h"
ERL_NIF_TERM saltywitch_shorthash_keygen(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM term;
(void) argc;
(void) argv;
unsigned char *key = enif_make_new_binary(env, crypto_shorthash_KEYBYTES, &term);
if (key == NULL) {
return saltywitch_exception(env, atom_error, atom_err_opaque);
}
crypto_shorthash_keygen(key);
return term;
}
ERL_NIF_TERM saltywitch_shorthash(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary key, message;
unsigned char *hash;
ERL_NIF_TERM term;
ERL_NIF_TERM err = atom_badarg;
ERL_NIF_TERM reason = atom_err_opaque;
(void) argc;
if (enif_inspect_binary(env, argv[0], &key) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
if (key.size != crypto_shorthash_KEYBYTES) {
reason = atom_err_invalid_key_size;
goto badarg_FAULT;
}
if (enif_inspect_binary(env, argv[1], &message) == false) {
reason = atom_err_invalid_type;
goto badarg_FAULT;
}
hash = enif_make_new_binary(env, crypto_shorthash_BYTES, &term);
if (hash == NULL) {
reason = atom_err_nif_alloc;
goto error_FAULT;
}
crypto_shorthash(hash, message.data, message.size, key.data);
return term;
error_FAULT:
err = atom_error;
badarg_FAULT:
return saltywitch_exception(env, err, reason);
}

5
clean.sh Executable file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env sh
for dir in ./build-*; do
[ -d $dir ] && rm -r $dir
done

18
lib/salty_witch.ex Normal file
View File

@ -0,0 +1,18 @@
defmodule SaltyWitch do
@moduledoc """
Documentation for `SaltyWitch`.
"""
@doc """
Returns version information
iex> SaltyWitch.info
{'1.0.18', 10, 3}
"""
def info do
SaltyWitch.NIF.info()
end
end

74
lib/salty_witch/nif.ex Normal file
View File

@ -0,0 +1,74 @@
defmodule SaltyWitch.NIF do
@compile {:autoload, false}
@on_load :__on_load__
def __on_load__() do
path = :filename.join(:code.priv_dir(:saltywitch), ~c"saltywitch")
:erlang.load_nif(path, 0)
end
def info, do: :erlang.nif_error(:not_loaded)
def randombytes_random, do: :erlang.nif_error(:not_loaded)
# random
def randombytes_random, do: :erlang.nif_error(:not_loaded)
def randombytes_uniform(upper)
def randombytes_uniform(_), do: :erlang.nif_error(:not_loaded)
def randombytes_buf(len)
def randombytes_buf(_), do: :erlang.nif_error(:not_loaded)
def randombytes_buf_deterministic(len, seed)
def randombytes_buf_deterministic(_, _), do: :erlang.nif_error(:not_loaded)
def randombytes_seedbytes, do: :erlang.nif_error(:not_loaded)
# secretbox
def secretbox_keygen, do: :erlang.nif_error(:not_loaded)
def secretbox_easy(message, nonce, key)
def secretbox_easy(_, _, _), do: :erlang.nif_error(:not_loaded)
def secretbox_open_easy(ciphertext, nonce, key)
def secretbox_open_easy(_, _, _), do: :erlang.nif_error(:not_loaded)
def secretbox_detached(message, nonce, key)
def secretbox_detached(_, _, _), do: :erlang.nif_error(:not_loaded)
def secretbox_open_detached(ciphertext, mac, nonce, key)
def secretbox_open_detached(_, _, _, _), do: :erlang.nif_error(:not_loaded)
def secretbox_keybytes, do: :erlang.nif_error(:not_loaded)
def secretbox_macbytes, do: :erlang.nif_error(:not_loaded)
def secretbox_noncebytes, do: :erlang.nif_error(:not_loaded)
# generichash
def generichash(_), do: :erlang.nif_error(:not_loaded)
def generichash(_, _), do: :erlang.nif_error(:not_loaded)
def generichash_keygen, do: :erlang.nif_error(:not_loaded)
def generichash_init, do: :erlang.nif_error(:not_loaded)
def generichash_init(_), do: :erlang.nif_error(:not_loaded)
def generichash_init(_, _), do: :erlang.nif_error(:not_loaded)
def generichash_update(_, _), do: :erlang.nif_error(:not_loaded)
def generichash_final(_), do: :erlang.nif_error(:not_loaded)
# shorthash
def shorthash_keygen, do: :erlang.nif_error(:not_loaded)
def shorthash(key, message)
def shorthash(_, _), do: :erlang.nif_error(:not_loaded)
# pwhash
def pwhash_str(pass, ops, mem)
def pwhash_str(_, _, _), do: :erlang.nif_error(:not_loaded)
def pwhash_str_verify(pwhash, pass)
def pwhash_str_verify(_, _), do: :erlang.nif_error(:not_loaded)
def pwhash_str_needs_rehash(pwhash, ops, mem)
def pwhash_str_needs_rehash(_, _, _), do: :erlang.nif_error(:not_loaded)
end

View File

@ -0,0 +1,11 @@
defmodule SaltyWitch.PasswordHash do
alias SaltyWitch.NIF, as: N
def string(pass, ops, mem) do
N.pwhash_str(pass, ops, mem)
end
def verify(pwhash, pass) do
N.pwhash_str_verify(pwhash, pass)
end
end

19
lib/salty_witch/random.ex Normal file
View File

@ -0,0 +1,19 @@
defmodule SaltyWitch.Random do
alias SaltyWitch.NIF, as: N
def seedbytes do
N.randombytes_seedbytes()
end
def uint32 do
N.randombytes_random()
end
def bytes(size) when size > 0 do
N.randombytes_buf(size)
end
def deterministic(size, seed) when size > 0 do
N.randombytes_buf_deterministic(size, seed)
end
end

View File

@ -0,0 +1,2 @@
defmodule SaltyWitch.SecretBox do
end

View File

@ -0,0 +1,9 @@
defmodule SaltyWitch.ShortHash do
def keygen do
SaltyWitch.NIF.shorthash_keygen()
end
def shorthash(key, message) do
SaltyWitch.NIF.shorthash(key, message)
end
end

169
meson.build Normal file
View File

@ -0,0 +1,169 @@
project('saltywitch', 'c',
version: 'trunk',
license: 'BSD-3-Clause',
meson_version: '>=1.1.0',
default_options: [
'c_std=c99',
'b_asneeded=true',
'b_lto=true',
'b_lundef=false',
'b_ndebug=if-release',
'b_pie=true',
'b_staticpic=true',
'warning_level=2',
'werror=false',
],
)
cc = meson.get_compiler('c')
# Default Flags
add_project_arguments(
cc.get_supported_arguments([
'-Walloca',
'-Warith-conversion',
'-Warray-bounds-pointer-arithmetic',
'-Warray-bounds=2',
'-Wbad-function-cast',
'-Wbitwise-instead-of-logical',
'-Wcast-align=strict',
'-Wcomma',
'-Wconditional-uninitialized',
'-Wconversion',
'-Wdate-time',
'-Wduplicated-branches',
'-Wduplicated-cond',
'-Wendif-labels',
'-Wenum-int-mismatch',
'-Wfloat-equal',
'-Wformat-overflow=2',
'-Wformat-security',
'-Wformat-truncation=2',
'-Wformat-type-confusion',
'-Wformat=2',
'-Wimplicit-fallthrough=5',
'-Wimplicit-int',
'-Winit-self',
'-Wjump-misses-init',
'-Wliteral-range',
'-Wlogical-op',
'-Wloop-analysis',
'-Wmaybe-unitialized',
'-Wmisleading-indentation',
'-Wmissing-include-dirs',
'-Wmissing-noreturn',
'-Wmissing-prototypes',
'-Wnested-externs',
'-Wno-format-nonliteral',
'-Wno-missing-braces',
'-Wno-missing-field-initializers',
'-Wno-typedef-redefinition',
'-Wnull-dereference',
'-Woverflow',
'-Wredundant-decls',
'-Wshadow',
'-Wshift-sign-overflow',
'-Wshorten-64-to-32',
'-Wsign-conversion',
'-Wsizeof-pointer-memaccess',
'-Wstack-usage=1000000',
'-Wstrict-overflow=3',
'-Wstrict-prototypes',
'-Wswitch-enum',
'-Wtautological-constant-in-range-compare',
'-Wthread-safety',
'-Wtraditional-conversion',
'-Wtrampolines',
'-Wundef',
'-Wuninitialized',
'-Wunused-but-set-variable',
'-Wuse-after-free=3',
'-Wvla',
'-Wwrite-strings',
'-Wno-error=#warnings',
'-Wno-error=attribute-warning',
'-Wno-error=unknown-attributes',
'-Wno-error=unknown-pragmas',
'-Wno-error=unused-parameter',
'-fcf-protection=full',
'-fno-common',
'-fno-delete-null-pointer-checks',
'-fno-strict-aliasing',
'-fstack-clash-protection',
'-fstack-protector',
'-fstack-protector-strong',
'-fwrapv',
'-fzero-call-used-regs=all',
]), language: 'c'
)
if get_option('buildtype') != 'debug'
add_project_arguments(
cc.get_supported_arguments([
'-ffunction-sections',
'-fdata-sections',
]), language: 'c'
)
add_project_link_arguments(
cc.get_supported_link_arguments([
'-Wl,--gc-sections',
'-Wl,-z,noexecstack',
'-Wl,-z,noexecheap',
'-Wl,-z,now',
'-Wl,-z,relro',
'-Wl,-z,separate-code',
]), language: 'c'
)
add_project_arguments([
'-D_FORTIFY_SOURCE=3',
], language: 'c'
)
endif
add_project_arguments([
'-D_DEFAULT_SOURCE',
], language: 'c'
)
erts_inc = get_option('erts_include_dir')
erl_interface_inc = get_option('erl_interface_include_dir')
erl_interface_lib_dir = get_option('erl_interface_lib_dir')
erl_interface_dep = cc.find_library('ei', dirs: [erl_interface_lib_dir], static: true, required: true)
sodium_dep = dependency('libsodium', version: '1.0.18', required: true)
saltywitch_src = files([
'c_src/saltywitch.c',
'c_src/atoms.c',
'c_src/auth.c',
'c_src/generichash.c',
'c_src/kx.c',
'c_src/pwhash.c',
'c_src/random.c',
'c_src/secretbox.c',
'c_src/shorthash.c',
])
saltywitch = shared_library(
'saltywitch',
saltywitch_src,
gnu_symbol_visibility: 'hidden',
include_directories: [ erl_interface_inc, erts_inc ],
dependencies: [ erl_interface_dep, sodium_dep ],
name_prefix: '',
install: true,
install_dir: 'priv',
)
summary(
{
'C compiler' : cc.get_id(),
'C linker' : cc.get_linker_id(),
}, bool_yn: true
)

4
meson.options Normal file
View File

@ -0,0 +1,4 @@
option('mix_target', type: 'string', description: '')
option('erts_include_dir', type: 'string', description: '')
option('erl_interface_lib_dir', type: 'string', description: '')
option('erl_interface_include_dir', type: 'string', description: '')

45
mix.exs Normal file
View File

@ -0,0 +1,45 @@
defmodule SaltyWitch.MixProject do
use Mix.Project
@version "0.0.1"
def project do
[
app: :saltywitch,
version: @version,
elixir: "~> 1.14",
compilers: [:elixir_make] ++ Mix.compilers(),
make_clean: ["clean"],
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
defp deps do
[
{:elixir_make, "~> 0.7", runtime: false}
]
end
defp package do
[
maintainers: ["Aydin Mercan"],
licenses: ["BSD-3-Clause"],
files: [
# Sources
"c_src",
"lib",
"mix.exs",
# Build
"CMakeLists.txt",
"Makefile",
"build.sh",
"clean.sh",
"meson.build",
# Meta
"LICENSE",
"README.md"
]
]
end
end

3
mix.lock Normal file
View File

@ -0,0 +1,3 @@
%{
"elixir_make": {:hex, :elixir_make, "0.7.6", "67716309dc5d43e16b5abbd00c01b8df6a0c2ab54a8f595468035a50189f9169", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5a0569756b0f7873a77687800c164cca6dfc03a09418e6fcf853d78991f49940"},
}

View File

@ -0,0 +1,8 @@
defmodule SaltyWitchTest do
use ExUnit.Case
doctest SaltyWitch
test "greets the world" do
assert SaltyWitch.hello() == :world
end
end

1
test/test_helper.exs Normal file
View File

@ -0,0 +1 @@
ExUnit.start()