diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..051d09d
--- /dev/null
+++ b/.envrc
@@ -0,0 +1 @@
+eval "$(lorri direnv)"
diff --git a/.github/workflows/Makefile b/.github/workflows/Makefile
new file mode 100644
index 0000000..b60a0c4
--- /dev/null
+++ b/.github/workflows/Makefile
@@ -0,0 +1,3 @@
+all:
+ cue export --out yaml > ci.yaml
+
diff --git a/.github/workflows/actions.cue b/.github/workflows/actions.cue
new file mode 100644
index 0000000..59482e6
--- /dev/null
+++ b/.github/workflows/actions.cue
@@ -0,0 +1,37 @@
+package actions
+
+#Name: string
+#Branches: branches: [...string]
+#Tags: tags: [...string]
+
+#On: {
+ push?: #Branches
+ pull_request?: #Branches
+ page_build?: #Branches
+}
+
+#Action: "actions/checkout@v2" | "erlef/setup-beam@v1"
+#Uses: {
+ uses: #Action
+ with?: {
+ ...
+ }
+}
+#Run: {
+ name: string
+ run: string
+}
+#Steps: #Uses | #Run
+
+#OS_Version: *"ubuntu-latest" | "macos-latest" | "windows_latest"
+
+#Jobs: ci: {
+ name: string
+ "runs-on": string
+ strategy:
+ matrix: {
+ otp_vsn: [...string]
+ os: [...#OS_Version]
+ }
+ steps: [...#Steps]
+}
\ No newline at end of file
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
new file mode 100644
index 0000000..0f20072
--- /dev/null
+++ b/.github/workflows/ci.yaml
@@ -0,0 +1,34 @@
+name: build
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+jobs:
+ ci:
+ name: Run checks and tests over ${{matrix.otp_vsn}} and ${{matrix.os}}
+ runs-on: ${{matrix.os}}
+ strategy:
+ matrix:
+ otp_vsn:
+ - "22.3"
+ - "23.3"
+ - "24.0"
+ os:
+ - ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: erlef/setup-beam@v1
+ with:
+ otp-version: ${{matrix.otp_vsn}}
+ rebar3-version: 3.16.1
+ - name: Update apt-get database
+ run: sudo apt-get update
+ - name: Install libsodium
+ run: sudo apt-get install -y libsodium-dev
+ - name: Compile source code
+ run: make compile
+ - name: Run the tests
+ run: make tests
diff --git a/.github/workflows/setup.cue b/.github/workflows/setup.cue
new file mode 100644
index 0000000..7fdea62
--- /dev/null
+++ b/.github/workflows/setup.cue
@@ -0,0 +1,21 @@
+package actions
+
+name: #Name & "build"
+on: #On & {
+ push: branches: [
+ _branch,
+ ]
+ pull_request: branches: [
+ _branch,
+ ]
+}
+
+jobs: #Jobs
+jobs: ci: {
+ name: "Run checks and tests over ${{matrix.otp_vsn}} and ${{matrix.os}}"
+ "runs-on": "${{matrix.os}}"
+ strategy: matrix: {
+ otp_vsn: _versions.otp
+ os: ["ubuntu-latest"]
+ }
+}
\ No newline at end of file
diff --git a/.github/workflows/steps.cue b/.github/workflows/steps.cue
new file mode 100644
index 0000000..840a6e5
--- /dev/null
+++ b/.github/workflows/steps.cue
@@ -0,0 +1,40 @@
+package actions
+
+// Versions for simplicity
+_versions: {
+ // The versions here have an underlying Debian/Ubuntu which support enough of
+ // libsodium to handle what enacl provides. Older versions will fail to compile
+ otp: ["22.3", "23.3", "24.0"]
+ rebar3: "3.16.1"
+}
+
+_branch: "master"
+
+jobs: ci: steps:
+[
+ {
+ uses: "actions/checkout@v2"
+ },
+ {
+ uses: "erlef/setup-beam@v1"
+ with: {
+ "otp-version": "${{matrix.otp_vsn}}"
+ "rebar3-version": _versions.rebar3
+ }
+ },
+ {
+ name: "Update apt-get database"
+ run: "sudo apt-get update"
+ },
+ {
+ name: "Install libsodium"
+ run: "sudo apt-get install -y libsodium-dev"
+ },
+ {
+ name: "Compile source code"
+ run: "make compile"
+ },
+ {
+ name: "Run the tests"
+ run: "make tests"
+ }]
diff --git a/.gitignore b/.gitignore
index a0ca3ba..870be65 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,4 +15,5 @@ _build
priv/enacl_nif.dll
priv/enacl_nif.exp
priv/enacl_nif.lib
-c_src/enacl_nif.d
+c_src/*.d
+
diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
new file mode 100644
index 0000000..b844a05
--- /dev/null
+++ b/.vscode/c_cpp_properties.json
@@ -0,0 +1,16 @@
+{
+ "configurations": [
+ {
+ "name": "Linux",
+ "includePath": [
+ "${workspaceFolder}/**"
+ ],
+ "defines": [],
+ "compilerPath": "/nix/store/fb30zc52va0g99q8qgv7kx4ngq163pii-gcc-wrapper-9.3.0/bin/gcc",
+ "cStandard": "c11",
+ "cppStandard": "c++17",
+ "intelliSenseMode": "clang-x64"
+ }
+ ],
+ "version": 4
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..691a8f6
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "C_Cpp.errorSquiggles": "Disabled"
+}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cb0bec4..2259d9b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,23 +5,139 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
-## [Unreleased]
+## [1.2.1]
+
+### Fixed [1.2.1]
+
+- Export types from the `enacl` module so it can be referenced in other parts of your system (serokell.io)
+
+## [1.2.0]
+
+### Fixed [1.2.0]
+
+- `sign_verify_detached/3` The code now verifies the size of signatures in detached mode. Before
+ this change, you could supply a larger binary and the code would only use the first `SIGNBYTES`
+ of the binary, assuming the signature were in there. Now, it fails with a badarg if the signature
+ doesn't match expectation in size.
+
+## [1.1.1]
+
+### Added [1.1.1]
+
+- Introduce the ability to reload the enacl module (Bryan Paxton, @starbelly)
+
+## [1.1.0]
+
+### Added [1.1.0]
+
+- Secretstream support was added to the API (Alexander Malaev)
+- Add KDF functions (Nicolas Goy, @kuon)
+- Add pwhash/5 specifying what algorithm to use for older compatibility (Nicolas Goy, @kuon)
+
+### Changed [1.1.0]
+
+- Remove rebar3_hex as a direct dependency (Bryan Paxton, @starbelly)
+
+## [1.0.0]
+
+### Compatibility [1.0.0]
+
+- Some functions have been streamlined to badarg in certain cases where it made more
+ sense to do so than returning back an error to the caller.
+- Functions generally don't return error values for internal errors. They now raise
+ exceptions when this happens. If you can't allocate a binary, there is usually not
+ much the programmer can do with that information, sans crashing.
+- If you used `aead_chacha20poly1305_*` functions, please read through the changelog
+ carefully as we have made changes to these functions. TL;DR: look for
+ `aead_chacha20poly1305_ietf_*` but note it is *not* just a simple substitution
+ into your code.
+- The `kx` constants have been renamed to follow libsodium one-to-one.
+- All calls with `verify` now returns booleans. See `sign_verify_detached`, which
+ were changed by this.
+- Many constants were changed to their underlying libsodium names.
+
+### Removed [1.0.0]
+
+- The functions of the form `aead_chacha20poly1305_*` were removed. They implement
+ the IETF variant, and the argument order for them were wrong. Also, they used
+ severely limited nonce values, which is somewhat dangerous. The `..._NONCEBYTES`
+ name was changed to the consistent `..._NPUBBYTES`.
+
+### Added [1.0.0]
+
+- Added `aead_chacha20poly1305_ietf_*` variants.
+- Implement multipart signature support, by Garry Hill.
+- Implement enacl:crypto_sign_seed_keypair/1, by Ole Andre Birkedal.
+- Implement enacl:crypto_sign_ed25519_sk_to_pk/1, by an anonymous contribution.
+- Added AEAD XChaCha20-Poly1305 support, thanks to Github/ECrownofFire.
+- The Password Hash Generation functions now support memory and operations limits,
+ thanks to Github/ECrownofFire.
+- Implement enacl:randombytes_uint32/0. Returns a random 32bit unsigned
+ integer, by means of the underlying random source.
+- Implement enacl:randombytes_uniform/1. Takes up to a 32bit unsigned
+ integer and produces a uniform integer in the range [0..N). Note
+ that the implementation avoids the typical non-uniformness which
+ would be present on a modulus operation on the nearest power-of-two
+ integer.
+- Added Win32 build support (Tino Breddin)
+- Added a nix shell for easier development
+
+### Changed [1.0.0]
+
+- Started a split the C code over multiple files for easier maintenance.
+- Rewrote the generichash routines to be more consistent. We are now more-or-less
+ following the style of the Erlang/OTP `crypto` library. While here, make sure
+ we clean up correctly and that we don't accidentally mis-ref-count data. The
+ code is a bit more goto heavy, but this style is surprisingly common in C code.
+- Use sodium's dynamic memory allocators. These guarantee 64bit alignment, and also
+ provide guard pages around the allocation, somewhat protecting it. It adds some
+ page table pressure compared to the current code, but is easier to maintain and
+ much cleaner code.
+- The code now rejects updates to generichash states which were already finalized.
+- We now track the desired outlen of a generichash operation in the opaque NIF
+ resource rather than on the Erlang side. This avoids some checks in the code,
+ and streamlines a good deal of the interface.
+- Split AEAD routines off from the main enacl_nif.c file
+- Renamed many routines from enif_* to enacl_*. This better reflects where they live
+ in the code base, and avoids pollution of the enif_* "namespace".
+- Split Sign Public Key routines from the rest. Modernize the handling of contexts.
+- The multi-part generic hash routines now follow the structure of the crypto
+ modules multi-part constructions in API and style.
+- The AEAD constructions have been streamlined so they follow the rules of libsodium
+ closer than before. In particular, some dead code has been removed as a result.
+- Constants are now named by their libsodium counterpart. This should make it easier
+ to find the correct names given the libsodium documentation.
+- Generichash now checks if a `_final` call has already happened and rejects further
+ hashing on the object. The rejection is an error: if you ever do this, your code
+ is definitely wrong and there is no recovery possible.
+
+### Fixed [1.0.0]
+
+- Fix a resource leak in generichash/sign init/update/final.
+- Clang static analysis warnings (Thomas Arts).
+- Replace a constant 31 with a computation from libsodium (Thomas Arts, from a security review).
+- Some subtle memory leaks in the error path for kx operations were plugged.
+- The multi-part generichash interface is now properly process/thread safe.
+- The sign interface is now properly process/thread safe.
## [0.17.2]
-### Fixed
+### Fixed [0.17.2]
+
- Work around `rebar3 hex` publishing .so files
## [0.17.1]
-### Fixed
+### Fixed [0.17.1]
+
- Provide a fix for the `pwhash_str/x` functions. The C strings were
not properly handled wrt. NULL-termination and what the libsodium
library expects.
## [0.17.0]
-### Added
+### Added [0.17.0]
+
- Expose the AEAD ChaCha20 Poly1305 (IETF) functionality (Hans
Svensson / Quviq).
- Expose Curve25519 Scalar Multiplication over a base point in the
@@ -33,11 +149,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
to verify the enacl library on embedded platforms and so on.
- Support generichash functions (Venkatakumar Srinivasan / Xaptum)
-### Fixed
+### Fixed [0.17.0]
+
- The type specification of generichash/2 and generichash/3 was
corrected (Technion)
-### Changed
+### Changed [0.17.0]
+
- Removed the experimental feature flag `ERL_NIF_DIRTY_JOB_CPU_BOUND`.
This breaks compatibility with older Erlang releases of dirty
schedulers, but prepares us correctly for the new releases where the
@@ -55,24 +173,26 @@ Bump libsodium requirement to version 1.0.12. This gives us access to
a number of functions which are added recently and thus gives us
access to implement these from libsodium.
-### Added
+### Added [0.16.0]
- Add kx_* functions (Alexander Malaev)
- chacha stream functions added, siphash-2-4 added, unsafe_memzero/1
added (no attribution)
-### Fixed
+### Fixed [0.16.0]
+
- Do not use the dirty-scheduler test macro as it is gone.
## [0.15.0]
-### Fixed
+### Fixed [0.15.0]
+
- Using `enacl:sign_verify_detacted` on large iolists would fail to do
the correct thing due to a typo. This has been corrected. Also the
EQC tests have been extended to include large binary support to
capture these kinds of errors in the future.
-### Changed
+### Changed [0.15.0]
- Many dirty-scheduler tunings have been performed to make sure we
won't block a scheduler ever.
@@ -88,33 +208,40 @@ a better citizen to other libraries and other parts of the system.
## [0.14.0]
-### Added
+### Added [0.14.0]
+
- Add support for libsodiums `box_seal` functions (Amir Ghassemi Nasr)
- Add support for libsodiums `crypto_sign_detached` (Joel Stanley,
Parnell Springmeyer)
-### Changed
+
+### Changed [0.14.0]
+
- Switch the tag names to the form `0.14.0` rather than `v0.14.0`. For
this release both tags are present, but from the next release on, it
won't be the case.
## [0.13.0]
-### Fixed
+### Fixed [0.13.0]
+
- Quell warnings from the C code
-### Added
+### Added [0.13.0]
+
- Add Ed 25519 utility API (Alexander Færøy)
- Add FreeBSD support for the NIF compilation (Ricardo Lanziano)
## [0.12.1]
-### Changed
+### Changed [0.12.1]
+
- Provide the `priv` directory for being able to properly build
without manual intervention.
## [0.12.0]
-### Added
+### Added [0.12.0]
+
- Introduce an extension interface for various necessary extensions to
the eNaCl system for handling the Tor network, thanks to Alexander
Færøy (ahf).
@@ -124,12 +251,15 @@ a better citizen to other libraries and other parts of the system.
## [0.11.0]
-### Added
+### Added [0.11.0]
+
- Introduce NIF layer beforenm/afternm calls.
- Introduce the API for precomputed keys (beforenm/afternm calls).
- Use test cases which tries to inject `iodata()` rather than binaries
in all places where `iodata()` tend to be accepted.
-### Fixed
+
+### Fixed [0.11.0]
+
- Fix type for `enacl:box_open/4`. The specification was wrong which
results in errors in other applications using enacl.
@@ -137,7 +267,8 @@ a better citizen to other libraries and other parts of the system.
Maintenance release. Fix some usability problems with the library.
-### Fixed
+### Fixed [0.10.2]
+
- Do not compile the C NIF code if there are no dirty scheduler
support in the Erlang system (Thanks to David N. Welton)
- Fix dialyzer warnings (Thanks Anthony Ramine)
@@ -151,7 +282,7 @@ Maintenance release. Fix some usability problems with the library.
## [0.10.1]
-### Added
+### Added [0.10.1]
- This small patch-release provides tests for the `randombytes/1`
function call, and optimizes EQC tests to make it easier to implement
@@ -167,15 +298,15 @@ included in this library.
Ultra-late beta; tuning for the last couple of functions which could
be nice to have.
-### Added
+### Added [0.10.0]
Added the function `randombytes/1` to obtain randombytes from the
operating system. The system uses the "best" applicable (P)RNG on the
target system:
-* Windows: `RtlGenRandom()`
-* OpenBSD, Bitrig: `arc4random()`
-* Unix in general: `/dev/urandom`
+- Windows: `RtlGenRandom()`
+- OpenBSD, Bitrig: `arc4random()`
+- Unix in general: `/dev/urandom`
Do note that on Linux and FreeBSD at the *least*, this is the best
thing you can do. Relying on `/dev/random` is almost always wrong and
@@ -189,4 +320,3 @@ Ultra-late beta. Code probably works, but it requires some real-world
use before it is deemed entirely stable.
Initial release.
-
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 2bac1c5..cfe84e4 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -3,9 +3,15 @@ List of people who have contributed to the eNaCl source code:
Alexander Færøy
Alexander Malaev
Amir Ghassemi Nasr
+Bryan Paxton
+GitHub/ECrownofFire
Geller Bedoya
Jesper Louis Andersen
Joel Stanley
Konrad Zemek
+Nicolas Goy
Parnell Springmeyer
Ricardo Lanziano
+Tino Breddin
+Venkatakumar Srinivasan
+
diff --git a/Makefile b/Makefile
index 481ed00..ce60591 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,10 @@ RUN_EQC=erl -pa _build/default/lib/enacl/ebin -noshell -s enacl_eqc -s init stop
compile:
$(REBAR) compile
+.PHONY: tests
+tests:
+ $(REBAR) ct
+
eqc_compile: compile
erlc -o _build/default/lib/enacl/ebin eqc_test/enacl_eqc.erl
diff --git a/README.md b/README.md
index 6855150..2b1457e 100644
--- a/README.md
+++ b/README.md
@@ -6,42 +6,40 @@ Frank Denis took the source and made it far more portable in the
libsodium library. The enacl project is somewhat misnamed, as it uses
libsodium as the underlying driver.
-Several Erlang ports of NaCl/libsodium exists, but this one is a
-rewrite with the following foci:
+## INSTALL/Requirements
-## INSTALL/Requirements:
+* New-ish Erlang installation. Tested back to version 22.3, but version 21 *may*
+ work as well.
+* *Requires* the libsodium library, and has been tested with version
+ 1.0.18. Lower versions might work, or they might fail to compile,
+ due to missing functionality. In particular, this means your libsodium installation
+ must be fairly recent as well.
+
+*Note:* If installing on systems which cuts packages into
+subpackages, make sure you also get the "-dev" package containing
+the header files necessary in order to compile software linking to
+libsodium.
-* Erlang/OTP 17.3. This library *needs* the newest dirty scheduler
- implementation. The library relies on dirty scheduler support in
- order to handle long-running cryptography jobs, by moving them off
- the main Erlang scheduler and letting the dirty schedulers handle
- the work. This keeps the Erlang VM responsive.
-* *Requires* the libsodium library, and at least in version 1.0.12.
- *Note:* If installing on systems which cuts packages into
- subpackages, make sure you also get the "-dev" package containing
- the header files necessary in order to compile software linking to
- libsodium.
-
To build the software execute:
- make
-
+ make
+
or
- rebar compile
+ rebar compile
To build and run licensed eqc test execute:
- make eqc_run
-
+ make eqc_run
+
To build and run eqc-mini version of test execute:
- make eqc_mini_run
-
-## Features:
+ make eqc_mini_run
+
+## Features
* Complete NaCl library, implementing all default functionality.
-* Implements a small set of additional functionality from libsodium.
+* Implements a large set of additional functionality from libsodium.
Most notably access to a proper CSPRNG random source
* Tests created by aggressive use of Erlang QuickCheck.
* NaCl is a very fast cryptographic library. That is,
@@ -60,22 +58,18 @@ In addition, I would like to thank Steve Vinoski, Rickard Green, and
Sverker Eriksson for providing the Dirty Scheduler API in the first
place.
-# USING:
+## Usage
-In general, consult the libsodium documentation at
-
- https://download.libsodium.org/doc/
+In general, consult the libsodium documentation at [Libsodium documentation](https://download.libsodium.org/doc/)
The original NaCl documentation is nowadays largely superceded by the
-libsodium documentation, but it is still worth a visit
+libsodium documentation, but it is still worth a visit [NaCl website](https://nacl.cr.yp.to)
- https://nacl.cr.yp.to
-
but also note that our interface has full Edoc documentation,
generated by executing
- rebar3 doc
-
+ rebar3 doc
+
## Hints
In general, the primitives provided by NaCl are intermediate-level
@@ -132,11 +126,11 @@ However, their correct use is still needed in order to be secure:
a foreign system as an oracle in order to learn the structure of a
string, breaking the cryptograhic system in the process.
-# Versions
+## Versions
See CHANGELOG.md
-# Overview
+## Overview
The NaCl cryptographic library provides a number of different
cryptographic primitives. In the following, we split up the different
@@ -198,7 +192,7 @@ This implements cryptography where there is a shared secret key between parties.
* *String comparison:* Implements guaranteed constant-time string
comparisons to protect against timing attacks.
-# Rationale
+## Rationale
Doing crypto right in Erlang is not that easy. For one, the crypto
system has to be rather fast, which rules out Erlang as the main
@@ -250,7 +244,7 @@ perhaps being able to switch faster. There are plans to rerun these
tests on OSX and Illumos as well, in order to investigate the numbers
on more platforms.
-# Testing
+## Testing
Every primitive has been stress-tested through the use of Erlang
QuickCheck with both *positive* and *negative* testing. This has been
@@ -277,11 +271,8 @@ sure we have no memory leaks as they will show themselves under the
extensive QuickCheck test cases we run. It has been verified there are
no leaks in the code.
-# Notes
+## Notes
[0] Other people have worked on bits and pieces of NaCl. These are
-just the 3 main authors. Please see the page
-
- http://nacl.cr.yp.to
-
+just the 3 main authors. Please see the page [NaCl](http://nacl.cr.yp.to)
for the full list of authors.
diff --git a/bench/timing.erl b/bench/timing.erl
index 4d004ea..0302e85 100644
--- a/bench/timing.erl
+++ b/bench/timing.erl
@@ -20,7 +20,7 @@ test() ->
randombytes() ->
randombytes(100*1000).
-
+
randombytes(0) -> ok;
randombytes(N) ->
enacl:randombytes(1024),
@@ -29,7 +29,7 @@ randombytes(N) ->
hash() ->
B = binary:copy(<<0>>, 4096),
hash(B, 10*1000).
-
+
hash(_B, 0) -> ok;
hash(B, N) ->
enacl:hash(B),
@@ -37,7 +37,7 @@ hash(B, N) ->
box_keypair() ->
box_keypair(10*1000).
-
+
box_keypair(0) -> ok;
box_keypair(N) ->
enacl:box_keypair(),
@@ -47,9 +47,9 @@ box() ->
#{ public := PK1} = enacl:box_keypair(),
#{ secret := SK2} = enacl:box_keypair(),
B = binary:copy(<<0>>, 1),
- Nonce = binary:copy(<<0>>, enacl:box_nonce_size()),
+ Nonce = binary:copy(<<0>>, enacl:box_NONCEBYTES()()),
box(B, Nonce, PK1, SK2, 10*1000).
-
+
box(_B, _Nonce, _PK1, _SK2, 0) -> ok;
box(B, Nonce, PK1, SK2, N) ->
enacl:box(B, Nonce, PK1, SK2),
@@ -62,15 +62,15 @@ box_before_after() ->
box_beforenm(PK1, SK2, 10*1000),
R = enacl:box_beforenm(PK1, SK2),
B = binary:copy(<<0>>, 8192),
- Nonce = binary:copy(<<0>>, enacl:box_nonce_size()),
+ Nonce = binary:copy(<<0>>, enacl:box_NONCEBYTES()()),
box_afternm(B, Nonce, R, 10*1000),
ok.
-
+
box_beforenm(_PK, _SK, 0) -> ok;
box_beforenm(PK, SK, N) ->
enacl:box_beforenm(PK, SK),
box_beforenm(PK, SK, N-1).
-
+
box_afternm(_Msg, _Nonce, _Key, 0) -> ok;
box_afternm(Msg, Nonce, Key, N) ->
enacl:box_afternm(Msg, Nonce, Key),
@@ -78,7 +78,7 @@ box_afternm(Msg, Nonce, Key, N) ->
sign_keypair() ->
sign_keypair(10*1000).
-
+
sign_keypair(0) -> ok;
sign_keypair(N) ->
enacl:sign_keypair(),
@@ -91,7 +91,7 @@ sign() ->
Msg = binary:copy(<<0>>, 1024),
#{ secret := SK } = enacl:sign_keypair(),
sign(Msg, SK, 10*1000).
-
+
sign(_Msg, _SK, 0) -> ok;
sign(Msg, SK, N) ->
enacl:sign(Msg, SK),
@@ -100,10 +100,10 @@ sign(Msg, SK, N) ->
secretbox() ->
Msg = binary:copy(<<0>>, 8192),
- Nonce = binary:copy(<<0>>, enacl:secretbox_nonce_size()),
- Key = binary:copy(<<0>>, enacl:secretbox_key_size()),
+ Nonce = binary:copy(<<0>>, enacl:secretbox_NONCEBYTES()()),
+ Key = binary:copy(<<0>>, enacl:secretbox_KEYBYTES()),
secretbox(Msg, Nonce, Key, 10*1000).
-
+
secretbox(_Msg, _Nonce, _Key, 0) -> ok;
secretbox(Msg, Nonce, Key, N) ->
enacl:secretbox(Msg, Nonce, Key),
@@ -111,8 +111,8 @@ secretbox(Msg, Nonce, Key, N) ->
stream() ->
- stream(16384, binary:copy(<<0>>, enacl:stream_nonce_size()), binary:copy(<<0>>, enacl:stream_key_size()), 10*1000).
-
+ stream(16384, binary:copy(<<0>>, enacl:stream_NONCEBYTES()), binary:copy(<<0>>, enacl:stream_KEYBYTES()), 10*1000).
+
stream(_L, _Nonce, _K, 0) -> ok;
stream(L, Nonce, K, N) ->
enacl:stream(L, Nonce, K),
@@ -120,31 +120,31 @@ stream(L, Nonce, K, N) ->
auth() ->
Msg = binary:copy(<<0>>, 4096),
- Key = binary:copy(<<0>>, enacl:auth_key_size()),
+ Key = binary:copy(<<0>>, enacl:auth_KEYBYTES()),
auth(Msg, Key, 10*1000).
-
+
auth(_Msg, _Key, 0) -> ok;
auth(Msg, Key, N) ->
enacl:auth(Msg, Key),
auth(Msg, Key, N-1).
-
+
onetime_auth() ->
Msg = binary:copy(<<0>>, 16384),
- Key = binary:copy(<<0>>, enacl:onetime_auth_key_size()),
+ Key = binary:copy(<<0>>, enacl:onetime_auth_KEYBYTES()),
onetime_auth(Msg, Key, 10*1000).
-
+
onetime_auth(_Msg, _Key, 0) -> ok;
onetime_auth(Msg, Key, N) ->
enacl:onetime_auth(Msg, Key),
onetime_auth(Msg, Key, N-1).
-
+
scalarmult() ->
Secret = binary:copy(<<0>>, 32),
BasePoint = binary:copy(<<1>>, 32),
scalarmult(Secret, BasePoint, 10*1000).
-
+
scalarmult(_S, _B, 0) -> ok;
scalarmult(S, B, N) ->
enacl:curve25519_scalarmult(S, B),
scalarmult(S, B, N-1).
-
+
diff --git a/c_src/aead.c b/c_src/aead.c
new file mode 100644
index 0000000..75851fd
--- /dev/null
+++ b/c_src/aead.c
@@ -0,0 +1,242 @@
+#include
+
+#include
+
+#include "aead.h"
+#include "enacl.h"
+
+/*
+ * AEAD ChaCha20 Poly1305
+ */
+ERL_NIF_TERM
+enacl_crypto_aead_chacha20poly1305_ietf_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_aead_chacha20poly1305_ietf_KEYBYTES);
+}
+
+ERL_NIF_TERM
+enacl_crypto_aead_chacha20poly1305_ietf_NPUBBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_aead_chacha20poly1305_ietf_NPUBBYTES);
+}
+
+ERL_NIF_TERM
+enacl_crypto_aead_chacha20poly1305_ietf_ABYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_aead_chacha20poly1305_ietf_ABYTES);
+}
+
+ERL_NIF_TERM
+enacl_crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX(
+ ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env,
+ crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX);
+}
+
+ERL_NIF_TERM
+enacl_crypto_aead_chacha20poly1305_ietf_encrypt(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ERL_NIF_TERM ret;
+ ErlNifBinary key, nonce, ad, message, ciphertext;
+
+ if (argc != 4)
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[0], &message))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[1], &ad))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[2], &nonce))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[3], &key))
+ goto bad_arg;
+ if (key.size != crypto_aead_chacha20poly1305_ietf_KEYBYTES)
+ goto bad_arg;
+ if (nonce.size != crypto_aead_chacha20poly1305_ietf_NPUBBYTES)
+ goto bad_arg;
+
+ if (!enif_alloc_binary(message.size +
+ crypto_aead_chacha20poly1305_ietf_ABYTES,
+ &ciphertext)) {
+ goto err;
+ }
+
+ crypto_aead_chacha20poly1305_ietf_encrypt(ciphertext.data, NULL, message.data,
+ message.size, ad.data, ad.size,
+ NULL, nonce.data, key.data);
+
+ ret = enif_make_binary(env, &ciphertext);
+ goto done;
+
+bad_arg:
+ return enif_make_badarg(env);
+err:
+ ret = enacl_internal_error(env);
+done:
+ return ret;
+}
+
+ERL_NIF_TERM
+enacl_crypto_aead_chacha20poly1305_ietf_decrypt(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ERL_NIF_TERM ret;
+ ErlNifBinary key, nonce, ad, message, ciphertext;
+
+ if (argc != 4)
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[0], &ciphertext))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[1], &ad))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[2], &nonce))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[3], &key))
+ goto bad_arg;
+
+ if (ciphertext.size < crypto_aead_chacha20poly1305_ietf_ABYTES)
+ goto bad_arg;
+ if (key.size != crypto_aead_chacha20poly1305_ietf_KEYBYTES)
+ goto bad_arg;
+ if (nonce.size != crypto_aead_chacha20poly1305_ietf_NPUBBYTES)
+ goto bad_arg;
+
+ if (!enif_alloc_binary(ciphertext.size -
+ crypto_aead_chacha20poly1305_ietf_ABYTES,
+ &message)) {
+ return enacl_internal_error(env);
+ }
+
+ if (crypto_aead_chacha20poly1305_ietf_decrypt(
+ message.data, NULL, NULL, ciphertext.data, ciphertext.size, ad.data,
+ ad.size, nonce.data, key.data) != 0) {
+ ret = enacl_error_tuple(env, "failed_verification");
+ goto release;
+ }
+
+ ret = enif_make_binary(env, &message);
+ goto done;
+bad_arg:
+ return enif_make_badarg(env);
+release:
+ enif_release_binary(&message);
+done:
+ return ret;
+}
+
+/*
+ * AEAD XChaCha20 Poly1305
+ */
+ERL_NIF_TERM
+enacl_crypto_aead_xchacha20poly1305_ietf_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_aead_xchacha20poly1305_ietf_KEYBYTES);
+}
+
+ERL_NIF_TERM
+enacl_crypto_aead_xchacha20poly1305_ietf_NPUBBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
+}
+
+ERL_NIF_TERM
+enacl_crypto_aead_xchacha20poly1305_ietf_ABYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_aead_xchacha20poly1305_ietf_ABYTES);
+}
+
+ERL_NIF_TERM
+enacl_crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX(
+ ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env,
+ crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX);
+}
+
+ERL_NIF_TERM
+enacl_crypto_aead_xchacha20poly1305_ietf_encrypt(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary key, nonce, ad, message, ciphertext;
+ ERL_NIF_TERM ret;
+
+ if (argc != 4)
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[0], &message))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[1], &ad))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[2], &nonce))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[3], &key))
+ goto bad_arg;
+
+ if (key.size != crypto_aead_xchacha20poly1305_ietf_KEYBYTES)
+ goto bad_arg;
+ if (nonce.size != crypto_aead_xchacha20poly1305_ietf_NPUBBYTES)
+ goto bad_arg;
+
+ if (!enif_alloc_binary(message.size +
+ crypto_aead_xchacha20poly1305_ietf_ABYTES,
+ &ciphertext)) {
+ goto err;
+ }
+
+ crypto_aead_xchacha20poly1305_ietf_encrypt(
+ ciphertext.data, NULL, message.data, message.size, ad.data, ad.size, NULL,
+ nonce.data, key.data);
+
+ ret = enif_make_binary(env, &ciphertext);
+ goto done;
+
+bad_arg:
+ return enif_make_badarg(env);
+err:
+ ret = enacl_internal_error(env);
+done:
+ return ret;
+}
+
+ERL_NIF_TERM
+enacl_crypto_aead_xchacha20poly1305_ietf_decrypt(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary key, nonce, ad, message, ciphertext;
+ ERL_NIF_TERM ret;
+
+ if (argc != 4)
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[0], &ciphertext))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[1], &ad))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[2], &nonce))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[3], &key))
+ goto bad_arg;
+
+ if (ciphertext.size < crypto_aead_xchacha20poly1305_ietf_ABYTES)
+ goto bad_arg;
+ if (key.size != crypto_aead_xchacha20poly1305_ietf_KEYBYTES)
+ goto bad_arg;
+ if (nonce.size != crypto_aead_xchacha20poly1305_ietf_NPUBBYTES)
+ goto bad_arg;
+
+ if (!enif_alloc_binary(ciphertext.size -
+ crypto_aead_xchacha20poly1305_ietf_ABYTES,
+ &message)) {
+ return enacl_internal_error(env);
+ }
+
+ if (crypto_aead_xchacha20poly1305_ietf_decrypt(
+ message.data, NULL, NULL, ciphertext.data, ciphertext.size, ad.data,
+ ad.size, nonce.data, key.data) != 0) {
+ ret = enacl_error_tuple(env, "failed_verification");
+ goto release;
+ }
+
+ ret = enif_make_binary(env, &message);
+ goto done;
+
+bad_arg:
+ return enif_make_badarg(env);
+release:
+ enif_release_binary(&message);
+done:
+ return ret;
+}
diff --git a/c_src/aead.h b/c_src/aead.h
new file mode 100644
index 0000000..1591b24
--- /dev/null
+++ b/c_src/aead.h
@@ -0,0 +1,46 @@
+#ifndef ENACL_AEAD_H
+#define ENACL_AEAD_H
+
+#include
+
+/* AEAD ChaCha20 Poly1305 */
+ERL_NIF_TERM
+enacl_crypto_aead_chacha20poly1305_ietf_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+ERL_NIF_TERM
+enacl_crypto_aead_chacha20poly1305_ietf_NPUBBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+ERL_NIF_TERM
+enacl_crypto_aead_chacha20poly1305_ietf_ABYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+ERL_NIF_TERM
+enacl_crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX(
+ ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]);
+ERL_NIF_TERM
+enacl_crypto_aead_chacha20poly1305_ietf_encrypt(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+ERL_NIF_TERM
+enacl_crypto_aead_chacha20poly1305_ietf_decrypt(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+/* AEAD XChaCha20 Poly1305 */
+ERL_NIF_TERM
+enacl_crypto_aead_xchacha20poly1305_ietf_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+ERL_NIF_TERM
+enacl_crypto_aead_xchacha20poly1305_ietf_NPUBBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+ERL_NIF_TERM
+enacl_crypto_aead_xchacha20poly1305_ietf_ABYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+ERL_NIF_TERM
+enacl_crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX(
+ ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]);
+ERL_NIF_TERM
+enacl_crypto_aead_xchacha20poly1305_ietf_encrypt(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+ERL_NIF_TERM
+enacl_crypto_aead_xchacha20poly1305_ietf_decrypt(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+#endif
diff --git a/c_src/enacl.c b/c_src/enacl.c
new file mode 100644
index 0000000..a27dd35
--- /dev/null
+++ b/c_src/enacl.c
@@ -0,0 +1,16 @@
+#include
+
+#include "enacl.h"
+
+ERL_NIF_TERM enacl_error_tuple(ErlNifEnv *env, char *error_atom) {
+ return enif_make_tuple2(env, enif_make_atom(env, "error"),
+ enif_make_atom(env, error_atom));
+}
+
+ERL_NIF_TERM enacl_internal_error(ErlNifEnv *env) {
+ return enif_raise_exception(env, enif_make_atom(env, "enacl_internal_error"));
+}
+
+ERL_NIF_TERM enacl_error_finalized(ErlNifEnv *env) {
+ return enif_raise_exception(env, enif_make_atom(env, "enacl_finalized"));
+}
\ No newline at end of file
diff --git a/c_src/enacl.h b/c_src/enacl.h
new file mode 100644
index 0000000..ef84b9e
--- /dev/null
+++ b/c_src/enacl.h
@@ -0,0 +1,15 @@
+#ifndef ENACL_H
+#define ENACL_H
+
+#include
+
+#define ATOM_OK "ok"
+#define ATOM_ERROR "error"
+#define ATOM_TRUE "true"
+#define ATOM_FALSE "false"
+
+ERL_NIF_TERM enacl_error_tuple(ErlNifEnv *, char *);
+ERL_NIF_TERM enacl_error_finalized(ErlNifEnv *);
+ERL_NIF_TERM enacl_internal_error(ErlNifEnv *);
+
+#endif
diff --git a/c_src/enacl_ext.c b/c_src/enacl_ext.c
new file mode 100644
index 0000000..167b784
--- /dev/null
+++ b/c_src/enacl_ext.c
@@ -0,0 +1,86 @@
+#include
+
+#include
+
+#include "enacl.h"
+#include "enacl_ext.h"
+
+static void uint64_pack(unsigned char *y, ErlNifUInt64 x) {
+ *y++ = x;
+ x >>= 8;
+ *y++ = x;
+ x >>= 8;
+ *y++ = x;
+ x >>= 8;
+ *y++ = x;
+ x >>= 8;
+ *y++ = x;
+ x >>= 8;
+ *y++ = x;
+ x >>= 8;
+ *y++ = x;
+ x >>= 8;
+ *y++ = x;
+}
+
+static ErlNifUInt64 uint64_unpack(const unsigned char *x) {
+ ErlNifUInt64 result;
+
+ result = x[7];
+ result <<= 8;
+ result |= x[6];
+ result <<= 8;
+ result |= x[5];
+ result <<= 8;
+ result |= x[4];
+ result <<= 8;
+ result |= x[3];
+ result <<= 8;
+ result |= x[2];
+ result <<= 8;
+ result |= x[1];
+ result <<= 8;
+ result |= x[0];
+ return result;
+}
+
+static int crypto_block(unsigned char *out, const unsigned char *in,
+ const unsigned char *k) {
+ ErlNifUInt64 v0 = uint64_unpack(in + 0);
+ ErlNifUInt64 v1 = uint64_unpack(in + 8);
+ ErlNifUInt64 k0 = uint64_unpack(k + 0);
+ ErlNifUInt64 k1 = uint64_unpack(k + 8);
+ ErlNifUInt64 k2 = uint64_unpack(k + 16);
+ ErlNifUInt64 k3 = uint64_unpack(k + 24);
+ ErlNifUInt64 sum = 0;
+ ErlNifUInt64 delta = 0x9e3779b97f4a7c15;
+ int i;
+ for (i = 0; i < 32; ++i) {
+ sum += delta;
+ v0 += ((v1 << 7) + k0) ^ (v1 + sum) ^ ((v1 >> 12) + k1);
+ v1 += ((v0 << 16) + k2) ^ (v0 + sum) ^ ((v0 >> 8) + k3);
+ }
+ uint64_pack(out + 0, v0);
+ uint64_pack(out + 8, v1);
+
+ return 0;
+}
+
+ERL_NIF_TERM enif_scramble_block_16(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary in, out, key;
+
+ if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &in)) ||
+ (!enif_inspect_binary(env, argv[1], &key)) || (in.size != 16) ||
+ (key.size != 32)) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(in.size, &out)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_block(out.data, in.data, key.data);
+
+ return enif_make_binary(env, &out);
+}
\ No newline at end of file
diff --git a/c_src/enacl_ext.h b/c_src/enacl_ext.h
new file mode 100644
index 0000000..c0e5a5a
--- /dev/null
+++ b/c_src/enacl_ext.h
@@ -0,0 +1,9 @@
+#ifndef ENACL_EXT_H
+#define ENACL_EXT_H
+
+#include
+
+ERL_NIF_TERM enif_scramble_block_16(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+#endif
diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c
index db34ab8..268096a 100644
--- a/c_src/enacl_nif.c
+++ b/c_src/enacl_nif.c
@@ -1,38 +1,43 @@
-#include "erl_nif.h"
-
+#include
#include
-#include
+#include
-#define ATOM_OK "ok"
-#define ATOM_ERROR "error"
-#define ATOM_TRUE "true"
-#define ATOM_FALSE "false"
-
-#define CRYPTO_GENERICHASH_STATE_RESOURCE "crypto_generichash_state"
+#include "aead.h"
+#include "enacl.h"
+#include "enacl_ext.h"
+#include "generichash.h"
+#include "hash.h"
+#include "kdf.h"
+#include "kx.h"
+#include "public.h"
+#include "pwhash.h"
+#include "randombytes.h"
+#include "secret.h"
+#include "secretstream.h"
+#include "sign.h"
#ifdef ERL_NIF_DIRTY_JOB_CPU_BOUND
-#define erl_nif_dirty_job_cpu_bound_macro(a,b,c) {a,b,c,ERL_NIF_DIRTY_JOB_CPU_BOUND}
+#define erl_nif_dirty_job_cpu_bound_macro(a, b, c) \
+ { a, b, c, ERL_NIF_DIRTY_JOB_CPU_BOUND }
#else
-#define erl_nif_dirty_job_cpu_bound_macro(a,b,c) {a,b,c}
+#define erl_nif_dirty_job_cpu_bound_macro(a, b, c) \
+ { a, b, c }
#endif
-//{"crypto_box_keypair", 0, enif_crypto_box_keypair, ERL_NIF_DIRTY_JOB_CPU_BOUND}
-/* Errors */
-
-/* This is a global variable for resource type */
-static ErlNifResourceType *generichash_state_type = NULL;
-
-static
-ERL_NIF_TERM nacl_error_tuple(ErlNifEnv *env, char *error_atom) {
- return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, error_atom));
-}
-
/* Initialization */
-static
-int enif_crypto_load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) {
- // Create a new resource type for crypto_generichash_state
- if( !(generichash_state_type = enif_open_resource_type(env, NULL, CRYPTO_GENERICHASH_STATE_RESOURCE, NULL, ERL_NIF_RT_CREATE, NULL)) ) {
+static int enacl_crypto_load(ErlNifEnv *env, void **priv_data,
+ ERL_NIF_TERM load_info) {
+ // Create a new resource type for crypto_generichash_state
+ if (!enacl_init_generic_hash_ctx(env)) {
+ return -1;
+ }
+
+ if (!enacl_init_sign_ctx(env)) {
+ return -1;
+ }
+
+ if (!enacl_init_secretstream_ctx(env)) {
return -1;
}
@@ -43,1736 +48,388 @@ int enif_crypto_load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) {
return 0;
}
-/* Low-level functions (Hashing, String Equality, ...) */
-
-static
-ERL_NIF_TERM enif_crypto_hash(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary input;
- ErlNifBinary result;
-
- if ((argc != 1) || (!enif_inspect_iolist_as_binary(env, argv[0], &input))) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(crypto_hash_BYTES, &result)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_hash(result.data, input.data, input.size);
-
- return enif_make_binary(env, &result);
+static int enacl_crypto_upgrade(ErlNifEnv* env, void **priv_data,
+ void **old_priv_data,
+ ERL_NIF_TERM load_info) {
+ return 0;
}
-static
-ERL_NIF_TERM enif_crypto_verify_16(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary x,y;
-
- if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &x))
- || (!enif_inspect_binary(env, argv[1], &y))) {
- return enif_make_badarg(env);
- }
-
- if (x.size != 16 || y.size != 16) {
- return enif_make_badarg(env);
- }
-
- if (0 == crypto_verify_16(x.data, y.data)) {
- return enif_make_atom(env, "true");
- } else {
- return enif_make_atom(env, "false");
- }
+static int enacl_crypto_unload(ErlNifEnv* env, void **priv_data,
+ ERL_NIF_TERM load_info) {
+ return 0;
}
-static
-ERL_NIF_TERM enif_crypto_verify_32(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary x,y;
+/* GENERAL ROUTINES
+ *
+ * These don't generally fit somewhere else nicely, so we keep them in the main
+ * file
+ *
+ */
+static ERL_NIF_TERM enacl_crypto_verify_16(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary x, y;
- if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &x))
- || (!enif_inspect_binary(env, argv[1], &y))) {
- return enif_make_badarg(env);
- }
+ if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &x)) ||
+ (!enif_inspect_binary(env, argv[1], &y))) {
+ return enif_make_badarg(env);
+ }
- if (x.size != 32 || y.size != 32) {
- return enif_make_badarg(env);
- }
+ if (x.size != 16 || y.size != 16) {
+ return enif_make_badarg(env);
+ }
- if (0 == crypto_verify_32(x.data, y.data)) {
- return enif_make_atom(env, "true");
- } else {
- return enif_make_atom(env, "false");
- }
+ if (0 == crypto_verify_16(x.data, y.data)) {
+ return enif_make_atom(env, "true");
+ } else {
+ return enif_make_atom(env, "false");
+ }
}
-/* This is very unsafe. It will not affect things that have been binary_copy()'ed
- Use this for destroying key material from ram but nothing more. Be careful! */
-static
-ERL_NIF_TERM enif_sodium_memzero(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary x;
+static ERL_NIF_TERM enacl_crypto_verify_32(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary x, y;
- if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &x))) {
- return enif_make_badarg(env);
- }
+ if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &x)) ||
+ (!enif_inspect_binary(env, argv[1], &y))) {
+ return enif_make_badarg(env);
+ }
- sodium_memzero(x.data,x.size);
+ if (x.size != 32 || y.size != 32) {
+ return enif_make_badarg(env);
+ }
+
+ if (0 == crypto_verify_32(x.data, y.data)) {
+ return enif_make_atom(env, "true");
+ } else {
+ return enif_make_atom(env, "false");
+ }
+}
+
+/* This is very unsafe. It will not affect things that have been
+ binary_copy()'ed Use this for destroying key material from ram but nothing
+ more. Be careful! */
+static ERL_NIF_TERM enif_sodium_memzero(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary x;
+
+ if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &x))) {
+ return enif_make_badarg(env);
+ }
+
+ sodium_memzero(x.data, x.size);
return enif_make_atom(env, "ok");
}
/* Curve 25519 */
-static
-ERL_NIF_TERM enif_crypto_curve25519_scalarmult(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ERL_NIF_TERM result;
- ErlNifBinary secret, basepoint, output;
- uint8_t bp[crypto_scalarmult_curve25519_BYTES];
-
- if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &secret))
- || (!enif_inspect_binary(env, argv[1], &basepoint))
- || (secret.size != crypto_scalarmult_curve25519_BYTES)
- || (basepoint.size != crypto_scalarmult_curve25519_BYTES)) {
- return enif_make_badarg(env);
- }
-
- memcpy(bp, basepoint.data, crypto_scalarmult_curve25519_BYTES);
-
- /* Clear the high-bit. Better safe than sorry. */
- bp[31] &= 0x7f;
-
- do
- {
- if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &output)) {
- result = nacl_error_tuple(env, "alloc_failed");
- continue;
- }
-
- if (crypto_scalarmult_curve25519(output.data, secret.data, bp) < 0) {
- result = nacl_error_tuple(env, "scalarmult_curve25519_failed");
- continue;
- }
-
- result = enif_make_binary(env, &output);
- } while (0);
-
- sodium_memzero(bp, crypto_scalarmult_curve25519_BYTES);
-
- return result;
-}
-
-static
-ERL_NIF_TERM enif_crypto_curve25519_scalarmult_base(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ERL_NIF_TERM result;
- ErlNifBinary secret, output;
-
- if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &secret))
- || (secret.size != crypto_scalarmult_curve25519_BYTES)) {
- return enif_make_badarg(env);
- }
-
- do
- {
- if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &output)) {
- result = nacl_error_tuple(env, "alloc_failed");
- continue;
- }
-
- if (crypto_scalarmult_curve25519_base(output.data, secret.data) < 0) {
- result = nacl_error_tuple(env, "scalarmult_curve25519_base_failed");
- continue;
- }
-
- result = enif_make_binary(env, &output);
- } while (0);
-
- return result;
-}
-
-/* Ed 25519 */
-static
-ERL_NIF_TERM enif_crypto_sign_ed25519_keypair(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary pk, sk;
-
- if (argc != 0) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(crypto_sign_ed25519_PUBLICKEYBYTES, &pk)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if (!enif_alloc_binary(crypto_sign_ed25519_SECRETKEYBYTES, &sk)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_sign_ed25519_keypair(pk.data, sk.data);
-
- return enif_make_tuple2(env, enif_make_binary(env, &pk), enif_make_binary(env, &sk));
-}
-
-static
-ERL_NIF_TERM enif_crypto_sign_ed25519_public_to_curve25519(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary curve25519_pk, ed25519_pk;
-
- if ((argc != 1)
- || (!enif_inspect_binary(env, argv[0], &ed25519_pk))
- || (ed25519_pk.size != crypto_sign_ed25519_PUBLICKEYBYTES)) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &curve25519_pk)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if (crypto_sign_ed25519_pk_to_curve25519(curve25519_pk.data, ed25519_pk.data) != 0) {
- return nacl_error_tuple(env, "ed25519_public_to_curve25519_failed");
- }
-
- return enif_make_binary(env, &curve25519_pk);
-}
-
-static
-ERL_NIF_TERM enif_crypto_sign_ed25519_secret_to_curve25519(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary curve25519_sk, ed25519_sk;
-
- if ((argc != 1)
- || (!enif_inspect_binary(env, argv[0], &ed25519_sk))
- || (ed25519_sk.size != crypto_sign_ed25519_SECRETKEYBYTES)) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &curve25519_sk)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if (crypto_sign_ed25519_sk_to_curve25519(curve25519_sk.data, ed25519_sk.data) != 0) {
- return nacl_error_tuple(env, "ed25519_secret_to_curve25519_failed");
- }
-
- return enif_make_binary(env, &curve25519_sk);
-}
-
-static
-ERL_NIF_TERM enif_crypto_sign_ed25519_PUBLICKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_sign_ed25519_PUBLICKEYBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_sign_ed25519_SECRETKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_sign_ed25519_SECRETKEYBYTES);
-}
-
-/* Public-key cryptography */
-static
-ERL_NIF_TERM enif_crypto_box_NONCEBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_box_NONCEBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_box_ZEROBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_box_ZEROBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_box_BOXZEROBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_box_BOXZEROBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_box_PUBLICKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_box_PUBLICKEYBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_box_SECRETKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_box_SECRETKEYBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_box_BEFORENMBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_box_BEFORENMBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_box_keypair(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary pk, sk;
-
- if (argc != 0) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(crypto_box_PUBLICKEYBYTES, &pk)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if (!enif_alloc_binary(crypto_box_SECRETKEYBYTES, &sk)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_box_keypair(pk.data, sk.data);
-
- return enif_make_tuple2(env, enif_make_binary(env, &pk), enif_make_binary(env, &sk));
-}
-
-static
-ERL_NIF_TERM enif_crypto_box(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary padded_msg, nonce, pk, sk, result;
-
- if (
- (argc != 4) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &padded_msg)) ||
- (!enif_inspect_binary(env, argv[1], &nonce)) ||
- (!enif_inspect_binary(env, argv[2], &pk)) ||
- (!enif_inspect_binary(env, argv[3], &sk))) {
- return enif_make_badarg(env);
- }
-
- if (
- (nonce.size != crypto_box_NONCEBYTES) ||
- (pk.size != crypto_box_PUBLICKEYBYTES) ||
- (sk.size != crypto_box_SECRETKEYBYTES) ||
- (padded_msg.size < crypto_box_ZEROBYTES)) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(padded_msg.size, &result)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if( 0 != crypto_box(result.data, padded_msg.data, padded_msg.size, nonce.data, pk.data, sk.data) ) {
- return nacl_error_tuple(env, "box_error");
- }
-
- return enif_make_sub_binary(
- env,
- enif_make_binary(env, &result),
- crypto_box_BOXZEROBYTES,
- padded_msg.size - crypto_box_BOXZEROBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_box_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary padded_ciphertext, nonce, pk, sk, result;
-
- if (
- (argc != 4) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &padded_ciphertext)) ||
- (!enif_inspect_binary(env, argv[1], &nonce)) ||
- (!enif_inspect_binary(env, argv[2], &pk)) ||
- (!enif_inspect_binary(env, argv[3], &sk))) {
- return enif_make_badarg(env);
- }
-
- if (
- (nonce.size != crypto_box_NONCEBYTES) ||
- (pk.size != crypto_box_PUBLICKEYBYTES) ||
- (sk.size != crypto_box_SECRETKEYBYTES) ||
- (padded_ciphertext.size < crypto_box_BOXZEROBYTES)) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(padded_ciphertext.size, &result)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if (0 != crypto_box_open(result.data, padded_ciphertext.data, padded_ciphertext.size, nonce.data, pk.data, sk.data)) {
- enif_release_binary(&result);
- return nacl_error_tuple(env, "failed_verification");
- }
-
- return enif_make_sub_binary(
- env,
- enif_make_binary(env, &result),
- crypto_box_ZEROBYTES,
- padded_ciphertext.size - crypto_box_ZEROBYTES);
-}
-
-/* Precomputed crypto boxes */
-
-static
-ERL_NIF_TERM enif_crypto_box_beforenm(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary k, pk, sk;
-
- if (
- (argc != 2) ||
- (!enif_inspect_binary(env, argv[0], &pk)) ||
- (!enif_inspect_binary(env, argv[1], &sk)) ||
- (pk.size != crypto_box_PUBLICKEYBYTES) ||
- (sk.size != crypto_box_SECRETKEYBYTES)) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(crypto_box_BEFORENMBYTES, &k)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if( 0 != crypto_box_beforenm(k.data, pk.data, sk.data) ) {
- // error
- return nacl_error_tuple(env, "error_gen_shared_secret");
- }
-
- return enif_make_binary(env, &k);
-}
-
-static
-ERL_NIF_TERM enif_crypto_box_afternm(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary result, m, nonce, k;
-
- if (
- (argc != 3) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
- (!enif_inspect_binary(env, argv[1], &nonce)) ||
- (!enif_inspect_binary(env, argv[2], &k)) ||
- (m.size < crypto_box_ZEROBYTES) ||
- (nonce.size != crypto_box_NONCEBYTES) ||
- (k.size != crypto_box_BEFORENMBYTES)) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(m.size, &result)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_box_afternm(result.data, m.data, m.size, nonce.data, k.data);
-
- return enif_make_sub_binary(
- env,
- enif_make_binary(env, &result),
- crypto_box_BOXZEROBYTES,
- m.size - crypto_box_BOXZEROBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_box_open_afternm(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary result, m, nonce, k;
-
- if (
- (argc != 3) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
- (!enif_inspect_binary(env, argv[1], &nonce)) ||
- (!enif_inspect_binary(env, argv[2], &k)) ||
- (m.size < crypto_box_BOXZEROBYTES) ||
- (nonce.size != crypto_box_NONCEBYTES) ||
- (k.size != crypto_box_BEFORENMBYTES)) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(m.size, &result)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if (0 != crypto_box_open_afternm(result.data, m.data, m.size, nonce.data, k.data)) {
- enif_release_binary(&result);
- return nacl_error_tuple(env, "failed_verification");
- }
-
- return enif_make_sub_binary(
- env,
- enif_make_binary(env, &result),
- crypto_box_ZEROBYTES,
- m.size - crypto_box_ZEROBYTES);
-}
-
-/* Signing */
-static
-ERL_NIF_TERM enif_crypto_sign_PUBLICKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_sign_PUBLICKEYBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_sign_SECRETKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_sign_SECRETKEYBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_sign_keypair(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary pk, sk;
-
- if (argc != 0) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(crypto_sign_PUBLICKEYBYTES, &pk)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if (!enif_alloc_binary(crypto_sign_SECRETKEYBYTES, &sk)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_sign_keypair(pk.data, sk.data);
-
- return enif_make_tuple2(env, enif_make_binary(env, &pk), enif_make_binary(env, &sk));
-}
-
-/*
-int crypto_sign(unsigned char *sm, unsigned long long *smlen,
- const unsigned char *m, unsigned long long mlen,
- const unsigned char *sk);
- */
-static
-ERL_NIF_TERM enif_crypto_sign(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary m, sk, sm;
- unsigned long long smlen;
-
- if (
- (argc != 2) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
- (!enif_inspect_binary(env, argv[1], &sk))) {
- return enif_make_badarg(env);
- }
-
- if (sk.size != crypto_sign_SECRETKEYBYTES) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(m.size + crypto_sign_BYTES, &sm)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_sign(sm.data, &smlen, m.data, m.size, sk.data);
-
- return enif_make_sub_binary(env, enif_make_binary(env, &sm), 0, smlen);
-}
-
-/*
-int crypto_sign_open(unsigned char *m, unsigned long long *mlen,
- const unsigned char *sm, unsigned long long smlen,
- const unsigned char *pk);
- */
-static
-ERL_NIF_TERM enif_crypto_sign_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary m, sm, pk;
- unsigned long long mlen;
-
- if (
- (argc != 2) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &sm)) ||
- (!enif_inspect_binary(env, argv[1], &pk))) {
- return enif_make_badarg(env);
- }
-
- if (pk.size != crypto_sign_PUBLICKEYBYTES) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(sm.size, &m)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if (0 == crypto_sign_open(m.data, &mlen, sm.data, sm.size, pk.data)) {
- return enif_make_sub_binary(env, enif_make_binary(env, &m), 0, mlen);
- } else {
- enif_release_binary(&m);
- return nacl_error_tuple(env, "failed_verification");
- }
-}
-
-/*
-int crypto_sign_detached(unsigned char *sig, unsigned long long *siglen,
- const unsigned char *m, unsigned long long mlen,
- const unsigned char *sk);
- */
-static
-ERL_NIF_TERM enif_crypto_sign_detached(ErlNifEnv* env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary m, sk, sig;
- unsigned long long siglen;
-
- if (
- (argc != 2) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
- (!enif_inspect_binary(env, argv[1], &sk))) {
- return enif_make_badarg(env);
- }
-
- if (sk.size != crypto_sign_SECRETKEYBYTES) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(crypto_sign_BYTES, &sig)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_sign_detached(sig.data, &siglen, m.data, m.size, sk.data);
-
- return enif_make_binary(env, &sig);
-}
-
-/*
-int crypto_sign_verify_detached(const unsigned char *sig,
- const unsigned char *m,
- unsigned long long mlen,
- const unsigned char *pk);
- */
-static
-ERL_NIF_TERM enif_crypto_sign_verify_detached(ErlNifEnv* env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary m, sig, pk;
-
- if (
- (argc != 3) ||
- (!enif_inspect_binary(env, argv[0], &sig)) ||
- (!enif_inspect_iolist_as_binary(env, argv[1], &m)) ||
- (!enif_inspect_binary(env, argv[2], &pk))) {
- return enif_make_badarg(env);
- }
-
- if (pk.size != crypto_sign_PUBLICKEYBYTES) {
- return enif_make_badarg(env);
- }
-
- if (0 == crypto_sign_verify_detached(sig.data, m.data, m.size, pk.data)) {
- return enif_make_atom(env, "true");
- } else {
- return enif_make_atom(env, "false");
- }
-}
-
-/* Sealed box functions */
-
-static
-ERL_NIF_TERM enif_crypto_box_SEALBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_box_SEALBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_box_seal(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary key, msg, ciphertext;
-
- if (
- (argc != 2) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &msg)) ||
- (!enif_inspect_binary(env, argv[1], &key))) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(msg.size + crypto_box_SEALBYTES, &ciphertext)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_box_seal(
- ciphertext.data,
- msg.data,
- msg.size,
- key.data);
-
- return enif_make_binary(env, &ciphertext);
-}
-
-
-static
-ERL_NIF_TERM enif_crypto_box_seal_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary pk, sk, ciphertext, msg;
-
- if (
- (argc != 3) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &ciphertext)) ||
- (!enif_inspect_binary(env, argv[1], &pk)) ||
- (!enif_inspect_binary(env, argv[2], &sk))) {
- return enif_make_badarg(env);
- }
-
- if (ciphertext.size < crypto_box_SEALBYTES) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(ciphertext.size - crypto_box_SEALBYTES, &msg)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if (crypto_box_seal_open(
- msg.data,
- ciphertext.data,
- ciphertext.size,
- pk.data,
- sk.data) != 0) {
- enif_release_binary(&msg);
- return nacl_error_tuple(env, "failed_verification");
- }
-
- return enif_make_binary(env, &msg);
-}
-
-/* Secret key cryptography */
-
-static
-ERL_NIF_TERM enif_crypto_secretbox_NONCEBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_secretbox_NONCEBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_secretbox_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_secretbox_KEYBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_secretbox_ZEROBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_secretbox_ZEROBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_secretbox_BOXZEROBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_secretbox_BOXZEROBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_stream_chacha20_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_stream_chacha20_KEYBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_stream_chacha20_NONCEBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_stream_chacha20_NONCEBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_stream_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_stream_KEYBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_stream_NONCEBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_stream_NONCEBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_auth_BYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_auth_BYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_auth_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_auth_KEYBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_shorthash_BYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_shorthash_BYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_shorthash_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_shorthash_KEYBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_onetimeauth_BYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_onetimeauth_BYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_onetimeauth_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_onetimeauth_KEYBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_secretbox(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary key, nonce, padded_msg, padded_ciphertext;
-
- if (
- (argc != 3) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &padded_msg)) ||
- (!enif_inspect_binary(env, argv[1], &nonce)) ||
- (!enif_inspect_binary(env, argv[2], &key))) {
- return enif_make_badarg(env);
- }
-
- if (
- (key.size != crypto_secretbox_KEYBYTES) ||
- (nonce.size != crypto_secretbox_NONCEBYTES) ||
- (padded_msg.size < crypto_secretbox_ZEROBYTES)) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(padded_msg.size, &padded_ciphertext)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_secretbox(
- padded_ciphertext.data,
- padded_msg.data, padded_msg.size,
- nonce.data,
- key.data);
-
- return enif_make_sub_binary(env,
- enif_make_binary(env, &padded_ciphertext),
- crypto_secretbox_BOXZEROBYTES,
- padded_msg.size - crypto_secretbox_BOXZEROBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_secretbox_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary key, nonce, padded_ciphertext, padded_msg;
-
- if (
- (argc != 3) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &padded_ciphertext)) ||
- (!enif_inspect_binary(env, argv[1], &nonce)) ||
- (!enif_inspect_binary(env, argv[2], &key))) {
- return enif_make_badarg(env);
- }
-
- if (
- (key.size != crypto_secretbox_KEYBYTES) ||
- (nonce.size != crypto_secretbox_NONCEBYTES) ||
- (padded_ciphertext.size < crypto_secretbox_BOXZEROBYTES)) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(padded_ciphertext.size, &padded_msg)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if (crypto_secretbox_open(
- padded_msg.data,
- padded_ciphertext.data,
- padded_ciphertext.size,
- nonce.data,
- key.data) != 0) {
- enif_release_binary(&padded_msg);
- return nacl_error_tuple(env, "failed_verification");
- }
-
- return enif_make_sub_binary(
- env,
- enif_make_binary(env, &padded_msg),
- crypto_secretbox_ZEROBYTES,
- padded_ciphertext.size - crypto_secretbox_ZEROBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_stream_chacha20(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary c, n, k;
- ErlNifUInt64 clen;
-
- if (
- (argc != 3) ||
- (!enif_get_uint64(env, argv[0], &clen)) ||
- (!enif_inspect_binary(env, argv[1], &n)) ||
- (!enif_inspect_binary(env, argv[2], &k))) {
- return enif_make_badarg(env);
- }
-
- if (
- (k.size != crypto_stream_chacha20_KEYBYTES) ||
- (n.size != crypto_stream_chacha20_NONCEBYTES)) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(clen, &c)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_stream_chacha20(c.data, c.size, n.data, k.data);
-
- return enif_make_binary(env, &c);
-}
-
-static
-ERL_NIF_TERM enif_crypto_stream_chacha20_xor(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary c, m, n, k;
-
- if (
- (argc != 3) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
- (!enif_inspect_binary(env, argv[1], &n)) ||
- (!enif_inspect_binary(env, argv[2], &k))) {
- return enif_make_badarg(env);
- }
-
- if (
- (k.size != crypto_stream_chacha20_KEYBYTES) ||
- (n.size != crypto_stream_chacha20_NONCEBYTES)) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(m.size, &c)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_stream_chacha20_xor(c.data, m.data, m.size, n.data, k.data);
-
- return enif_make_binary(env, &c);
-}
-
-static
-ERL_NIF_TERM enif_crypto_stream(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary c, n, k;
- ErlNifUInt64 clen;
-
- if (
- (argc != 3) ||
- (!enif_get_uint64(env, argv[0], &clen)) ||
- (!enif_inspect_binary(env, argv[1], &n)) ||
- (!enif_inspect_binary(env, argv[2], &k))) {
- return enif_make_badarg(env);
- }
-
- if (
- (k.size != crypto_stream_KEYBYTES) ||
- (n.size != crypto_stream_NONCEBYTES)) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(clen, &c)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_stream(c.data, c.size, n.data, k.data);
-
- return enif_make_binary(env, &c);
-}
-
-static
-ERL_NIF_TERM enif_crypto_stream_xor(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary c, m, n, k;
-
- if (
- (argc != 3) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
- (!enif_inspect_binary(env, argv[1], &n)) ||
- (!enif_inspect_binary(env, argv[2], &k))) {
- return enif_make_badarg(env);
- }
-
- if (
- (k.size != crypto_stream_KEYBYTES) ||
- (n.size != crypto_stream_NONCEBYTES)) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(m.size, &c)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_stream_xor(c.data, m.data, m.size, n.data, k.data);
-
- return enif_make_binary(env, &c);
-}
-
-static
-ERL_NIF_TERM enif_crypto_auth(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary a,m,k;
-
- if (
- (argc != 2) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
- (!enif_inspect_binary(env, argv[1], &k))) {
- return enif_make_badarg(env);
- }
-
- if (k.size != crypto_auth_KEYBYTES) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(crypto_auth_BYTES, &a)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_auth(a.data, m.data, m.size, k.data);
-
- return enif_make_binary(env, &a);
-}
-
-static
-ERL_NIF_TERM enif_crypto_auth_verify(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary a, m, k;
-
- if (
- (argc != 3) ||
- (!enif_inspect_binary(env, argv[0], &a)) ||
- (!enif_inspect_iolist_as_binary(env, argv[1], &m)) ||
- (!enif_inspect_binary(env, argv[2], &k))) {
- return enif_make_badarg(env);
- }
-
- if (
- (k.size != crypto_auth_KEYBYTES) ||
- (a.size != crypto_auth_BYTES)) {
- return enif_make_badarg(env);
- }
-
- if (0 == crypto_auth_verify(a.data, m.data, m.size, k.data)) {
- return enif_make_atom(env, "true");
- } else {
- return enif_make_atom(env, "false");
- }
-}
-
-static
-ERL_NIF_TERM enif_crypto_shorthash(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary a,m,k;
-
- if (
- (argc != 2) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
- (!enif_inspect_binary(env, argv[1], &k))) {
- return enif_make_badarg(env);
- }
-
- if (k.size != crypto_shorthash_KEYBYTES) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(crypto_shorthash_BYTES, &a)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_shorthash(a.data, m.data, m.size, k.data);
-
- return enif_make_binary(env, &a);
-}
-
-static
-ERL_NIF_TERM enif_crypto_onetimeauth(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary a,m,k;
-
- if (
- (argc != 2) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
- (!enif_inspect_binary(env, argv[1], &k))) {
- return enif_make_badarg(env);
- }
-
- if (k.size != crypto_onetimeauth_KEYBYTES) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(crypto_onetimeauth_BYTES, &a)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_onetimeauth(a.data, m.data, m.size, k.data);
-
- return enif_make_binary(env, &a);
-}
-
-static
-ERL_NIF_TERM enif_crypto_onetimeauth_verify(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary a, m, k;
-
- if (
- (argc != 3) ||
- (!enif_inspect_binary(env, argv[0], &a)) ||
- (!enif_inspect_iolist_as_binary(env, argv[1], &m)) ||
- (!enif_inspect_binary(env, argv[2], &k))) {
- return enif_make_badarg(env);
- }
-
- if (
- (k.size != crypto_onetimeauth_KEYBYTES) ||
- (a.size != crypto_onetimeauth_BYTES)) {
- return enif_make_badarg(env);
- }
-
- if (0 == crypto_onetimeauth_verify(a.data, m.data, m.size, k.data)) {
- return enif_make_atom(env, "true");
- } else {
- return enif_make_atom(env, "false");
- }
-}
-
-static
-ERL_NIF_TERM enif_randombytes(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[])
-{
- unsigned req_size;
- ErlNifBinary result;
-
- if ((argc != 1) || (!enif_get_uint(env, argv[0], &req_size))) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(req_size, &result)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- randombytes(result.data, result.size);
-
- return enif_make_binary(env, &result);
-}
-
-/* Key exchange */
-
-static
-ERL_NIF_TERM enif_crypto_kx_SECRETKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_kx_SECRETKEYBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_kx_PUBLICKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_kx_PUBLICKEYBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_kx_SESSIONKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_kx_SESSIONKEYBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_kx_keypair(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[])
-{
- ErlNifBinary pk, sk;
-
- if (argc != 0) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(crypto_kx_PUBLICKEYBYTES, &pk)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if (!enif_alloc_binary(crypto_kx_SECRETKEYBYTES, &sk)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_kx_keypair(pk.data, sk.data);
-
- return enif_make_tuple2(env, enif_make_binary(env, &pk), enif_make_binary(env, &sk));
-}
-
-static
-ERL_NIF_TERM enif_crypto_kx_server_session_keys(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[])
-{
- ErlNifBinary rx, tx, server_pk, server_sk, client_pk;
-
- if ((argc != 3) ||
- (!enif_inspect_binary(env, argv[0], &server_pk)) ||
- (!enif_inspect_binary(env, argv[1], &server_sk)) ||
- (!enif_inspect_binary(env, argv[2], &client_pk)) ||
- (server_pk.size != crypto_kx_PUBLICKEYBYTES) ||
- (server_sk.size != crypto_kx_SECRETKEYBYTES) ||
- (client_pk.size != crypto_kx_PUBLICKEYBYTES)) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &rx)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &tx)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if( 0 != crypto_kx_server_session_keys(rx.data, tx.data, server_pk.data, server_sk.data, client_pk.data) ) {
- // suspicious client public key
- return nacl_error_tuple(env, "invalid_client_public_key");
- }
-
- return enif_make_tuple2(env, enif_make_binary(env, &rx), enif_make_binary(env, &tx));
-}
-
-static
-ERL_NIF_TERM enif_crypto_kx_client_session_keys(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[])
-{
- ErlNifBinary rx, tx, client_pk, client_sk, server_pk;
-
- if ((argc != 3) ||
- (!enif_inspect_binary(env, argv[0], &client_pk)) ||
- (!enif_inspect_binary(env, argv[1], &client_sk)) ||
- (!enif_inspect_binary(env, argv[2], &server_pk)) ||
- (client_pk.size != crypto_kx_PUBLICKEYBYTES) ||
- (client_sk.size != crypto_kx_SECRETKEYBYTES) ||
- (server_pk.size != crypto_kx_PUBLICKEYBYTES)) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &rx)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &tx)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if( 0 != crypto_kx_client_session_keys(rx.data, tx.data, client_pk.data, client_sk.data, server_pk.data) ) {
- // suspicious server public key
- return nacl_error_tuple(env, "invalid_server_public_key");
- }
-
- return enif_make_tuple2(env, enif_make_binary(env, &rx), enif_make_binary(env, &tx));
-}
-
-/* Various other helper functions */
-static
-void uint64_pack(unsigned char *y, ErlNifUInt64 x)
-{
- *y++ = x; x >>= 8;
- *y++ = x; x >>= 8;
- *y++ = x; x >>= 8;
- *y++ = x; x >>= 8;
- *y++ = x; x >>= 8;
- *y++ = x; x >>= 8;
- *y++ = x; x >>= 8;
- *y++ = x; x >>= 8;
-}
-
-static
-ErlNifUInt64 uint64_unpack(const unsigned char *x)
-{
- ErlNifUInt64 result;
-
- result = x[7];
- result <<= 8; result |= x[6];
- result <<= 8; result |= x[5];
- result <<= 8; result |= x[4];
- result <<= 8; result |= x[3];
- result <<= 8; result |= x[2];
- result <<= 8; result |= x[1];
- result <<= 8; result |= x[0];
- return result;
-}
-
-static
-int crypto_block(unsigned char *out, const unsigned char *in, const unsigned char *k)
-{
- ErlNifUInt64 v0 = uint64_unpack(in + 0);
- ErlNifUInt64 v1 = uint64_unpack(in + 8);
- ErlNifUInt64 k0 = uint64_unpack(k + 0);
- ErlNifUInt64 k1 = uint64_unpack(k + 8);
- ErlNifUInt64 k2 = uint64_unpack(k + 16);
- ErlNifUInt64 k3 = uint64_unpack(k + 24);
- ErlNifUInt64 sum = 0;
- ErlNifUInt64 delta = 0x9e3779b97f4a7c15;
- int i;
- for (i = 0;i < 32;++i) {
- sum += delta;
- v0 += ((v1<<7) + k0) ^ (v1 + sum) ^ ((v1>>12) + k1);
- v1 += ((v0<<16) + k2) ^ (v0 + sum) ^ ((v0>>8) + k3);
- }
- uint64_pack(out + 0,v0);
- uint64_pack(out + 8,v1);
-
- return 0;
-}
-
-static
-ERL_NIF_TERM enif_scramble_block_16(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[])
-{
- ErlNifBinary in, out, key;
-
- if (
- (argc != 2) ||
- (!enif_inspect_binary(env, argv[0], &in)) ||
- (!enif_inspect_binary(env, argv[1], &key)) ||
- (in.size != 16) || (key.size != 32)) {
- return enif_make_badarg(env);
- }
-
- if (!enif_alloc_binary(in.size, &out)) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- crypto_block(out.data, in.data, key.data);
-
- return enif_make_binary(env, &out);
-}
-
-static
-ERL_NIF_TERM enif_crypto_pwhash(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary h, p, s;
-
- // Validate the arguments
- if( (argc != 2) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &p)) ||
- (!enif_inspect_binary(env, argv[1], &s)) ) {
- return enif_make_badarg(env);
- }
-
- // Check Salt size
- if(s.size != crypto_pwhash_SALTBYTES) {
- return nacl_error_tuple(env, "invalid_salt_size");
- }
-
- // Allocate memory for return binary
- if( !enif_alloc_binary(crypto_box_SEEDBYTES, &h) ) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if( crypto_pwhash(h.data, h.size, (char *)p.data, p.size, s.data,
- crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE, crypto_pwhash_ALG_DEFAULT) != 0) {
- /* out of memory */
- enif_release_binary(&h);
- return nacl_error_tuple(env, "out_of_memory");
- }
-
- ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK);
- ERL_NIF_TERM ret = enif_make_binary(env, &h);
-
- return enif_make_tuple2(env, ok, ret);
-}
-
-static
-ERL_NIF_TERM enif_crypto_pwhash_str(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary h, p;
-
- // Validate the arguments
- if( (argc != 1) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &p)) ) {
- return enif_make_badarg(env);
- }
-
- // Allocate memory for return binary
- if( !enif_alloc_binary(crypto_pwhash_STRBYTES, &h) ) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- if( crypto_pwhash_str((char *)h.data, (char *)p.data, p.size,
- crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE) != 0) {
- /* out of memory */
- enif_release_binary(&h);
- return nacl_error_tuple(env, "out_of_memory");
- }
-
- ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK);
- ERL_NIF_TERM ret = enif_make_binary(env, &h);
-
- return enif_make_tuple2(env, ok, ret);
-}
-
-static
-ERL_NIF_TERM enif_crypto_pwhash_str_verify(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary h, p;
-
- // Validate the arguments
- if( (argc != 2) ||
- (!enif_inspect_iolist_as_binary(env, argv[0], &h)) ||
- (!enif_inspect_iolist_as_binary(env, argv[1], &p)) ) {
- return enif_make_badarg(env);
- }
-
- ERL_NIF_TERM retVal = enif_make_atom(env, ATOM_TRUE);
- if( crypto_pwhash_str_verify((char *)h.data, (char *)p.data, p.size) != 0) {
- /* wrong password */
- retVal = enif_make_atom(env, ATOM_FALSE);
- }
-
- return retVal;
-}
-
-/*
- * AEAD ChaCha20 Poly1305
- */
-static
-ERL_NIF_TERM enif_crypto_aead_chacha20poly1305_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_aead_chacha20poly1305_ietf_KEYBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_aead_chacha20poly1305_NPUBBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_aead_chacha20poly1305_ietf_NPUBBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_aead_chacha20poly1305_ABYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_aead_chacha20poly1305_ietf_ABYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX);
-}
-
-static
-ERL_NIF_TERM enif_crypto_aead_chacha20poly1305_encrypt(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
+static ERL_NIF_TERM
+enacl_crypto_curve25519_scalarmult(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
ERL_NIF_TERM result;
- ErlNifBinary key, nonce, ad, message, ciphertext;
+ ErlNifBinary secret, basepoint, output;
+ uint8_t bp[crypto_scalarmult_curve25519_BYTES];
- if ((argc != 4) || (!enif_inspect_binary(env, argv[0], &key))
- || (!enif_inspect_binary(env, argv[1], &nonce))
- || (!enif_inspect_binary(env, argv[2], &ad))
- || (!enif_inspect_binary(env, argv[3], &message))
- || (key.size != crypto_aead_chacha20poly1305_ietf_KEYBYTES)
- || (nonce.size != crypto_aead_chacha20poly1305_ietf_NPUBBYTES)) {
- return enif_make_badarg(env);
+ if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &secret)) ||
+ (!enif_inspect_binary(env, argv[1], &basepoint)) ||
+ (secret.size != crypto_scalarmult_curve25519_BYTES) ||
+ (basepoint.size != crypto_scalarmult_curve25519_BYTES)) {
+ return enif_make_badarg(env);
}
- do
- {
- if (!enif_alloc_binary(message.size + crypto_aead_chacha20poly1305_ietf_ABYTES, &ciphertext)) {
- result = nacl_error_tuple(env, "alloc_failed");
- continue;
- }
+ memcpy(bp, basepoint.data, crypto_scalarmult_curve25519_BYTES);
- if (crypto_aead_chacha20poly1305_ietf_encrypt(ciphertext.data, NULL, message.data, message.size,
- ad.data, ad.size, NULL, nonce.data, key.data) < 0) {
- result = nacl_error_tuple(env, "aead_chacha20poly1305_ietf_encrypt_failed");
- continue;
- }
+ /* Clear the high-bit. Better safe than sorry. */
+ bp[crypto_scalarmult_curve25519_BYTES - 1] &= 0x7f;
- result = enif_make_binary(env, &ciphertext);
+ do {
+ if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &output)) {
+ result = enacl_internal_error(env);
+ continue;
+ }
+
+ if (crypto_scalarmult_curve25519(output.data, secret.data, bp) != 0) {
+ enif_release_binary(&output);
+ result = enacl_error_tuple(env, "scalarmult_curve25519_failed");
+ continue;
+ }
+
+ result = enif_make_binary(env, &output);
+ } while (0);
+
+ sodium_memzero(bp, crypto_scalarmult_curve25519_BYTES);
+
+ return result;
+}
+
+static ERL_NIF_TERM
+enacl_crypto_curve25519_scalarmult_base(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ERL_NIF_TERM result;
+ ErlNifBinary secret, output;
+
+ if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &secret)) ||
+ (secret.size != crypto_scalarmult_curve25519_BYTES)) {
+ return enif_make_badarg(env);
+ }
+
+ do {
+ if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &output)) {
+ result = enacl_internal_error(env);
+ continue;
+ }
+
+ if (crypto_scalarmult_curve25519_base(output.data, secret.data) != 0) {
+ enif_release_binary(&output);
+ result = enacl_error_tuple(env, "scalarmult_curve25519_base_failed");
+ continue;
+ }
+
+ result = enif_make_binary(env, &output);
} while (0);
return result;
-
-}
-
-static
-ERL_NIF_TERM enif_crypto_aead_chacha20poly1305_decrypt(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ERL_NIF_TERM result;
- ErlNifBinary key, nonce, ad, message, ciphertext;
-
- if ((argc != 4) || (!enif_inspect_binary(env, argv[0], &key))
- || (!enif_inspect_binary(env, argv[1], &nonce))
- || (!enif_inspect_binary(env, argv[2], &ad))
- || (!enif_inspect_binary(env, argv[3], &ciphertext))
- || (ciphertext.size < crypto_aead_chacha20poly1305_ietf_ABYTES)
- || (key.size != crypto_aead_chacha20poly1305_ietf_KEYBYTES)
- || (nonce.size != crypto_aead_chacha20poly1305_ietf_NPUBBYTES)) {
- return enif_make_badarg(env);
- }
-
- do
- {
- if (!enif_alloc_binary(ciphertext.size - crypto_aead_chacha20poly1305_ietf_ABYTES, &message)) {
- result = nacl_error_tuple(env, "alloc_failed");
- continue;
- }
-
- if (crypto_aead_chacha20poly1305_ietf_decrypt(message.data, NULL, NULL, ciphertext.data, ciphertext.size,
- ad.data, ad.size, nonce.data, key.data) < 0) {
- result = nacl_error_tuple(env, "aead_chacha20poly1305_ietf_decrypt_failed");
- continue;
- }
-
- result = enif_make_binary(env, &message);
- } while (0);
-
- return result;
-
-}
-
-/*
- * Generic hash
- */
-static
-ERL_NIF_TERM enif_crypto_generichash_BYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_generichash_BYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_generichash_BYTES_MIN(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_generichash_BYTES_MIN);
-}
-
-static
-ERL_NIF_TERM enif_crypto_generichash_BYTES_MAX(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_generichash_BYTES_MAX);
-}
-
-static
-ERL_NIF_TERM enif_crypto_generichash_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_generichash_KEYBYTES);
-}
-
-static
-ERL_NIF_TERM enif_crypto_generichash_KEYBYTES_MIN(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_generichash_KEYBYTES_MIN);
-}
-
-static
-ERL_NIF_TERM enif_crypto_generichash_KEYBYTES_MAX(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- return enif_make_int64(env, crypto_generichash_KEYBYTES_MAX);
-}
-
-static
-ERL_NIF_TERM enif_crypto_generichash(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary hash, message, key;
-
- unsigned hashSize;
-
- // Validate the arguments
- if( (argc != 3) ||
- (!enif_get_uint(env, argv[0], &hashSize)) ||
- (!enif_inspect_binary(env, argv[1], &message)) ||
- (!enif_inspect_binary(env, argv[2], &key)) ) {
- return enif_make_badarg(env);
- }
-
- // Verify that hash size is crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX
- if( (hashSize < crypto_generichash_BYTES_MIN) ||
- (hashSize > crypto_generichash_BYTES_MAX) ) {
- return nacl_error_tuple(env, "invalid_hash_size");
- }
-
- // validate key size
- unsigned char *k = key.data;
- if( 0 == key.size ) {
- k = NULL;
- } else if( key.size < crypto_generichash_KEYBYTES_MIN || key.size > crypto_generichash_KEYBYTES_MAX ) {
- return nacl_error_tuple(env, "invalid_key_size");
- }
-
- // allocate memory for hash
- if( !enif_alloc_binary(hashSize, &hash) ) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- // calculate hash
- if( 0 != crypto_generichash(hash.data, hash.size, message.data, message.size, k, key.size) ) {
- enif_release_binary(&hash);
- return nacl_error_tuple(env, "hash_error");
- }
-
- ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK);
- ERL_NIF_TERM ret = enif_make_binary(env, &hash);
-
- return enif_make_tuple2(env, ok, ret);
-}
-
-static
-crypto_generichash_state *align64(void *ptr){
- if((unsigned long)ptr % 64 == 0)
- return ptr;
- return (unsigned long)ptr + (64 - ((unsigned long)ptr % 64));
-}
-
-static
-ERL_NIF_TERM enif_crypto_generichash_init(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary key;
-
- unsigned hashSize;
-
- // Validate the arguments
- if( (argc != 2) ||
- (!enif_get_uint(env, argv[0], &hashSize)) ||
- (!enif_inspect_binary(env, argv[1], &key)) ) {
- return enif_make_badarg(env);
- }
-
- // Verify that hash size is crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX
- if( (hashSize < crypto_generichash_BYTES_MIN) ||
- (hashSize > crypto_generichash_BYTES_MAX) ) {
- return nacl_error_tuple(env, "invalid_hash_size");
- }
-
- // validate key size
- unsigned char *k = key.data;
- if( 0 == key.size ) {
- k = NULL;
- } else if( key.size < crypto_generichash_KEYBYTES_MIN || key.size > crypto_generichash_KEYBYTES_MAX ) {
- return nacl_error_tuple(env, "invalid_key_size");
- }
-
- // Create a resource for hash state (+ 60 to make room for 64-byte alignment)
- void *state = enif_alloc_resource(generichash_state_type, crypto_generichash_statebytes() + 60);
- if( !state ) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- // Call the library function
- if( 0 != crypto_generichash_init(align64(state), k, key.size, hashSize) ) {
- return nacl_error_tuple(env, "hash_init_error");
- }
-
-
- // Create return values
- ERL_NIF_TERM e1 = enif_make_atom(env, "hashstate");
- ERL_NIF_TERM e2 = argv[0];
- ERL_NIF_TERM e3 = enif_make_resource(env, state);
-
-
- // release dynamically allocated memory to erlang to mange
- enif_release_resource(state);
-
- // return a tuple
- return enif_make_tuple3(env, e1, e2, e3);
-}
-
-static
-ERL_NIF_TERM enif_crypto_generichash_update(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary message;
-
- unsigned hashSize;
-
- void *state;
-
- // Validate the arguments
- if( (argc != 3) ||
- (!enif_get_uint(env, argv[0], &hashSize)) ||
- (!enif_get_resource(env, argv[1], generichash_state_type, (void **)&state)) ||
- (!enif_inspect_binary(env, argv[2], &message)) ) {
- return enif_make_badarg(env);
- }
-
- // Verify that hash size is crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX
- if( (hashSize < crypto_generichash_BYTES_MIN) ||
- (hashSize > crypto_generichash_BYTES_MAX) ) {
- return nacl_error_tuple(env, "invalid_hash_size");
- }
-
- // Update hash state
- if( 0 != crypto_generichash_update(align64(state), message.data, message.size) ) {
- return nacl_error_tuple(env, "hash_update_error");
- }
-
- // Generate return value
- ERL_NIF_TERM e1 = enif_make_atom(env, "hashstate");
- ERL_NIF_TERM e2 = argv[0];
- ERL_NIF_TERM e3 = enif_make_resource(env, state);
-
- // return a tuple
- return enif_make_tuple3(env, e1, e2, e3);
-}
-
-static
-ERL_NIF_TERM enif_crypto_generichash_final(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
- ErlNifBinary hash;
-
- unsigned hashSize;
-
- void *state;
-
- // Validate the arguments
- if( (argc != 2) ||
- (!enif_get_uint(env, argv[0], &hashSize)) ||
- (!enif_get_resource(env, argv[1], generichash_state_type, (void **)&state)) ) {
- return enif_make_badarg(env);
- }
-
- // Verify that hash size is crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX
- if( (hashSize < crypto_generichash_BYTES_MIN) ||
- (hashSize > crypto_generichash_BYTES_MAX) ) {
- return nacl_error_tuple(env, "invalid_hash_size");
- }
-
- // allocate memory for hash
- if( !enif_alloc_binary(hashSize, &hash) ) {
- return nacl_error_tuple(env, "alloc_failed");
- }
-
- // calculate hash
- if( 0 != crypto_generichash_final(align64(state), hash.data, hash.size) ) {
- enif_release_binary(&hash);
- return nacl_error_tuple(env, "hash_error");
- }
-
- ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK);
- ERL_NIF_TERM ret = enif_make_binary(env, &hash);
-
- return enif_make_tuple2(env, ok, ret);
}
/* Tie the knot to the Erlang world */
static ErlNifFunc nif_funcs[] = {
- {"crypto_box_NONCEBYTES", 0, enif_crypto_box_NONCEBYTES},
- {"crypto_box_ZEROBYTES", 0, enif_crypto_box_ZEROBYTES},
- {"crypto_box_BOXZEROBYTES", 0, enif_crypto_box_BOXZEROBYTES},
- {"crypto_box_PUBLICKEYBYTES", 0, enif_crypto_box_PUBLICKEYBYTES},
- {"crypto_box_SECRETKEYBYTES", 0, enif_crypto_box_SECRETKEYBYTES},
- {"crypto_box_BEFORENMBYTES", 0, enif_crypto_box_BEFORENMBYTES},
+ {"crypto_box_NONCEBYTES", 0, enacl_crypto_box_NONCEBYTES},
+ {"crypto_box_ZEROBYTES", 0, enacl_crypto_box_ZEROBYTES},
+ {"crypto_box_BOXZEROBYTES", 0, enacl_crypto_box_BOXZEROBYTES},
+ {"crypto_box_PUBLICKEYBYTES", 0, enacl_crypto_box_PUBLICKEYBYTES},
+ {"crypto_box_SECRETKEYBYTES", 0, enacl_crypto_box_SECRETKEYBYTES},
+ {"crypto_box_BEFORENMBYTES", 0, enacl_crypto_box_BEFORENMBYTES},
- erl_nif_dirty_job_cpu_bound_macro("crypto_box_keypair", 0, enif_crypto_box_keypair),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_box_keypair", 0,
+ enacl_crypto_box_keypair),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_box", 4, enacl_crypto_box),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_box_open", 4,
+ enacl_crypto_box_open),
- erl_nif_dirty_job_cpu_bound_macro("crypto_box", 4, enif_crypto_box),
- erl_nif_dirty_job_cpu_bound_macro("crypto_box_open", 4, enif_crypto_box_open),
+ {"crypto_box_beforenm", 2, enacl_crypto_box_beforenm},
+ {"crypto_box_afternm_b", 3, enacl_crypto_box_afternm},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_box_afternm", 3,
+ enacl_crypto_box_afternm),
+ {"crypto_box_open_afternm_b", 3, enacl_crypto_box_open_afternm},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_box_open_afternm", 3,
+ enacl_crypto_box_open_afternm),
- {"crypto_box_beforenm", 2, enif_crypto_box_beforenm},
- {"crypto_box_afternm_b", 3, enif_crypto_box_afternm},
- erl_nif_dirty_job_cpu_bound_macro("crypto_box_afternm", 3, enif_crypto_box_afternm),
- {"crypto_box_open_afternm_b", 3, enif_crypto_box_open_afternm},
- erl_nif_dirty_job_cpu_bound_macro("crypto_box_open_afternm", 3, enif_crypto_box_open_afternm),
+ {"crypto_sign_PUBLICKEYBYTES", 0, enacl_crypto_sign_PUBLICKEYBYTES},
+ {"crypto_sign_SECRETKEYBYTES", 0, enacl_crypto_sign_SECRETKEYBYTES},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_sign_keypair", 0,
+ enacl_crypto_sign_keypair),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_sign_seed_keypair", 1,
+ enacl_crypto_sign_seed_keypair),
- {"crypto_sign_PUBLICKEYBYTES", 0, enif_crypto_sign_PUBLICKEYBYTES},
- {"crypto_sign_SECRETKEYBYTES", 0, enif_crypto_sign_SECRETKEYBYTES},
- erl_nif_dirty_job_cpu_bound_macro("crypto_sign_keypair", 0, enif_crypto_sign_keypair),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_sign", 2, enacl_crypto_sign),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_sign_open", 2,
+ enacl_crypto_sign_open),
- erl_nif_dirty_job_cpu_bound_macro("crypto_sign", 2, enif_crypto_sign),
- erl_nif_dirty_job_cpu_bound_macro("crypto_sign_open", 2, enif_crypto_sign_open),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_sign_detached", 2,
+ enacl_crypto_sign_detached),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_sign_verify_detached", 3,
+ enacl_crypto_sign_verify_detached),
+ {"crypto_sign_init", 0, enacl_crypto_sign_init},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_sign_update", 2,
+ enacl_crypto_sign_update),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_sign_final_create", 2,
+ enacl_crypto_sign_final_create),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_sign_final_verify", 3,
+ enacl_crypto_sign_final_verify),
- erl_nif_dirty_job_cpu_bound_macro("crypto_sign_detached", 2, enif_crypto_sign_detached),
- erl_nif_dirty_job_cpu_bound_macro("crypto_sign_verify_detached", 3, enif_crypto_sign_verify_detached),
+ {"crypto_sign_ed25519_sk_to_pk", 1, enacl_crypto_sign_ed25519_sk_to_pk},
- {"crypto_box_SEALBYTES", 0, enif_crypto_box_SEALBYTES},
+ {"crypto_box_SEALBYTES", 0, enacl_crypto_box_SEALBYTES},
- erl_nif_dirty_job_cpu_bound_macro("crypto_box_seal", 2, enif_crypto_box_seal),
- erl_nif_dirty_job_cpu_bound_macro("crypto_box_seal_open", 3, enif_crypto_box_seal_open),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_box_seal", 2,
+ enacl_crypto_box_seal),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_box_seal_open", 3,
+ enacl_crypto_box_seal_open),
- {"crypto_secretbox_NONCEBYTES", 0, enif_crypto_secretbox_NONCEBYTES},
- {"crypto_secretbox_ZEROBYTES", 0, enif_crypto_secretbox_ZEROBYTES},
- {"crypto_secretbox_BOXZEROBYTES", 0, enif_crypto_secretbox_BOXZEROBYTES},
- {"crypto_secretbox_KEYBYTES", 0, enif_crypto_secretbox_KEYBYTES},
- {"crypto_secretbox_b", 3, enif_crypto_secretbox},
- erl_nif_dirty_job_cpu_bound_macro("crypto_secretbox", 3, enif_crypto_secretbox),
- {"crypto_secretbox_open_b", 3, enif_crypto_secretbox_open},
- erl_nif_dirty_job_cpu_bound_macro("crypto_secretbox_open", 3, enif_crypto_secretbox_open),
+ {"crypto_secretbox_NONCEBYTES", 0, enacl_crypto_secretbox_NONCEBYTES},
+ {"crypto_secretbox_ZEROBYTES", 0, enacl_crypto_secretbox_ZEROBYTES},
+ {"crypto_secretbox_BOXZEROBYTES", 0, enacl_crypto_secretbox_BOXZEROBYTES},
+ {"crypto_secretbox_KEYBYTES", 0, enacl_crypto_secretbox_KEYBYTES},
+ {"crypto_secretbox_b", 3, enacl_crypto_secretbox},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_secretbox", 3,
+ enacl_crypto_secretbox),
+ {"crypto_secretbox_open_b", 3, enacl_crypto_secretbox_open},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_secretbox_open", 3,
+ enacl_crypto_secretbox_open),
- {"crypto_stream_chacha20_KEYBYTES", 0, enif_crypto_stream_chacha20_KEYBYTES},
- {"crypto_stream_chacha20_NONCEBYTES", 0, enif_crypto_stream_chacha20_NONCEBYTES},
- {"crypto_stream_chacha20_b", 3, enif_crypto_stream_chacha20},
- erl_nif_dirty_job_cpu_bound_macro("crypto_stream_chacha20", 3, enif_crypto_stream_chacha20),
- {"crypto_stream_chacha20_xor_b", 3, enif_crypto_stream_chacha20_xor},
- erl_nif_dirty_job_cpu_bound_macro("crypto_stream_chacha20_xor", 3, enif_crypto_stream_chacha20_xor),
+ {"crypto_stream_chacha20_KEYBYTES", 0,
+ enacl_crypto_stream_chacha20_KEYBYTES},
+ {"crypto_stream_chacha20_NONCEBYTES", 0,
+ enacl_crypto_stream_chacha20_NONCEBYTES},
+ {"crypto_stream_chacha20_b", 3, enacl_crypto_stream_chacha20},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_stream_chacha20", 3,
+ enacl_crypto_stream_chacha20),
+ {"crypto_stream_chacha20_xor_b", 3, enacl_crypto_stream_chacha20_xor},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_stream_chacha20_xor", 3,
+ enacl_crypto_stream_chacha20_xor),
- {"crypto_stream_KEYBYTES", 0, enif_crypto_stream_KEYBYTES},
- {"crypto_stream_NONCEBYTES", 0, enif_crypto_stream_NONCEBYTES},
- {"crypto_stream_b", 3, enif_crypto_stream},
- erl_nif_dirty_job_cpu_bound_macro("crypto_stream", 3, enif_crypto_stream),
- {"crypto_stream_xor_b", 3, enif_crypto_stream_xor},
- erl_nif_dirty_job_cpu_bound_macro("crypto_stream_xor", 3, enif_crypto_stream_xor),
+ {"crypto_stream_KEYBYTES", 0, enacl_crypto_stream_KEYBYTES},
+ {"crypto_stream_NONCEBYTES", 0, enacl_crypto_stream_NONCEBYTES},
+ {"crypto_stream_b", 3, enacl_crypto_stream},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_stream", 3, enacl_crypto_stream),
+ {"crypto_stream_xor_b", 3, enacl_crypto_stream_xor},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_stream_xor", 3,
+ enacl_crypto_stream_xor),
- {"crypto_auth_BYTES", 0, enif_crypto_auth_BYTES},
- {"crypto_auth_KEYBYTES", 0, enif_crypto_auth_KEYBYTES},
- {"crypto_auth_b", 2, enif_crypto_auth},
- erl_nif_dirty_job_cpu_bound_macro("crypto_auth", 2, enif_crypto_auth),
- {"crypto_auth_verify_b", 3, enif_crypto_auth_verify},
- erl_nif_dirty_job_cpu_bound_macro("crypto_auth_verify", 3, enif_crypto_auth_verify),
+ {"crypto_auth_BYTES", 0, enacl_crypto_auth_BYTES},
+ {"crypto_auth_KEYBYTES", 0, enacl_crypto_auth_KEYBYTES},
+ {"crypto_auth_b", 2, enacl_crypto_auth},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_auth", 2, enacl_crypto_auth),
+ {"crypto_auth_verify_b", 3, enacl_crypto_auth_verify},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_auth_verify", 3,
+ enacl_crypto_auth_verify),
- {"crypto_shorthash_BYTES", 0, enif_crypto_shorthash_BYTES},
- {"crypto_shorthash_KEYBYTES", 0, enif_crypto_shorthash_KEYBYTES},
- {"crypto_shorthash", 2, enif_crypto_shorthash},
+ {"crypto_shorthash_BYTES", 0, enacl_crypto_shorthash_BYTES},
+ {"crypto_shorthash_KEYBYTES", 0, enacl_crypto_shorthash_KEYBYTES},
+ {"crypto_shorthash", 2, enacl_crypto_shorthash},
- {"crypto_onetimeauth_BYTES", 0, enif_crypto_onetimeauth_BYTES},
- {"crypto_onetimeauth_KEYBYTES", 0, enif_crypto_onetimeauth_KEYBYTES},
- {"crypto_onetimeauth_b", 2, enif_crypto_onetimeauth},
- erl_nif_dirty_job_cpu_bound_macro("crypto_onetimeauth", 2, enif_crypto_onetimeauth),
- {"crypto_onetimeauth_verify_b", 3, enif_crypto_onetimeauth_verify},
- erl_nif_dirty_job_cpu_bound_macro("crypto_onetimeauth_verify", 3, enif_crypto_onetimeauth_verify),
+ {"crypto_onetimeauth_BYTES", 0, enacl_crypto_onetimeauth_BYTES},
+ {"crypto_onetimeauth_KEYBYTES", 0, enacl_crypto_onetimeauth_KEYBYTES},
+ {"crypto_onetimeauth_b", 2, enacl_crypto_onetimeauth},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_onetimeauth", 2,
+ enacl_crypto_onetimeauth),
+ {"crypto_onetimeauth_verify_b", 3, enacl_crypto_onetimeauth_verify},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_onetimeauth_verify", 3,
+ enacl_crypto_onetimeauth_verify),
- {"crypto_hash_b", 1, enif_crypto_hash},
- erl_nif_dirty_job_cpu_bound_macro("crypto_hash", 1, enif_crypto_hash),
- {"crypto_verify_16", 2, enif_crypto_verify_16},
- {"crypto_verify_32", 2, enif_crypto_verify_32},
- {"sodium_memzero", 1, enif_sodium_memzero},
+ {"crypto_hash_b", 1, enacl_crypto_hash},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_hash", 1, enacl_crypto_hash),
+ {"crypto_verify_16", 2, enacl_crypto_verify_16},
+ {"crypto_verify_32", 2, enacl_crypto_verify_32},
+ {"sodium_memzero", 1, enif_sodium_memzero},
- {"crypto_pwhash", 2, enif_crypto_pwhash},
- {"crypto_pwhash_str", 1, enif_crypto_pwhash_str},
- {"crypto_pwhash_str_verify", 2, enif_crypto_pwhash_str_verify},
+ {"crypto_pwhash_SALTBYTES", 0, enacl_crypto_pwhash_SALTBYTES},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_pwhash", 5, enacl_crypto_pwhash),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_pwhash_str", 3,
+ enacl_crypto_pwhash_str),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_pwhash_str_verify", 2,
+ enacl_crypto_pwhash_str_verify),
- erl_nif_dirty_job_cpu_bound_macro("crypto_curve25519_scalarmult", 2, enif_crypto_curve25519_scalarmult),
- erl_nif_dirty_job_cpu_bound_macro("crypto_curve25519_scalarmult_base", 1, enif_crypto_curve25519_scalarmult_base),
+ {"crypto_kdf_KEYBYTES", 0, enacl_crypto_kdf_KEYBYTES},
+ {"crypto_kdf_CONTEXTBYTES", 0, enacl_crypto_kdf_CONTEXTBYTES},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_kdf_derive_from_key", 3,
+ enacl_crypto_kdf_derive_from_key),
- erl_nif_dirty_job_cpu_bound_macro("crypto_sign_ed25519_keypair", 0, enif_crypto_sign_ed25519_keypair),
- {"crypto_sign_ed25519_public_to_curve25519", 1, enif_crypto_sign_ed25519_public_to_curve25519},
- {"crypto_sign_ed25519_secret_to_curve25519", 1, enif_crypto_sign_ed25519_secret_to_curve25519},
- {"crypto_sign_ed25519_PUBLICKEYBYTES", 0, enif_crypto_sign_ed25519_PUBLICKEYBYTES},
- {"crypto_sign_ed25519_SECRETKEYBYTES", 0, enif_crypto_sign_ed25519_SECRETKEYBYTES},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_curve25519_scalarmult", 2,
+ enacl_crypto_curve25519_scalarmult),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_curve25519_scalarmult_base", 1,
+ enacl_crypto_curve25519_scalarmult_base),
- erl_nif_dirty_job_cpu_bound_macro("randombytes", 1, enif_randombytes),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_sign_ed25519_keypair", 0,
+ enacl_crypto_sign_ed25519_keypair),
+ {"crypto_sign_ed25519_public_to_curve25519", 1,
+ enacl_crypto_sign_ed25519_public_to_curve25519},
+ {"crypto_sign_ed25519_secret_to_curve25519", 1,
+ enacl_crypto_sign_ed25519_secret_to_curve25519},
+ {"crypto_sign_ed25519_PUBLICKEYBYTES", 0,
+ enacl_crypto_sign_ed25519_PUBLICKEYBYTES},
+ {"crypto_sign_ed25519_SECRETKEYBYTES", 0,
+ enacl_crypto_sign_ed25519_SECRETKEYBYTES},
- erl_nif_dirty_job_cpu_bound_macro("crypto_kx_keypair", 0, enif_crypto_kx_keypair),
- erl_nif_dirty_job_cpu_bound_macro("crypto_kx_client_session_keys", 3, enif_crypto_kx_client_session_keys),
- erl_nif_dirty_job_cpu_bound_macro("crypto_kx_server_session_keys", 3, enif_crypto_kx_server_session_keys),
- {"crypto_kx_PUBLICKEYBYTES", 0, enif_crypto_kx_PUBLICKEYBYTES},
- {"crypto_kx_SECRETKEYBYTES", 0, enif_crypto_kx_SECRETKEYBYTES},
- {"crypto_kx_SESSIONKEYBYTES", 0, enif_crypto_kx_SESSIONKEYBYTES},
+ // Linux might block here if early in the boot sequence, so get it off the
+ // main scheduler. Otherwise, it it would probably be fine to run on the
+ // main scheduler. This plays it safe, albeit with a performance hit.
+ //
+ // However: you shouldn't use a CSPRNG unless you need one. So it is
+ // probably fine to do the dirty-scheduler dance. Using the random
+ // material should dwarf the extraction of random material.
+ erl_nif_dirty_job_cpu_bound_macro("randombytes", 1, enif_randombytes),
+ erl_nif_dirty_job_cpu_bound_macro("randombytes_uint32", 0,
+ enif_randombytes_uint32),
+ erl_nif_dirty_job_cpu_bound_macro("randombytes_uniform", 1,
+ enif_randombytes_uniform),
- {"scramble_block_16", 2, enif_scramble_block_16},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_kx_keypair", 0,
+ enacl_crypto_kx_keypair),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_kx_client_session_keys", 3,
+ enacl_crypto_kx_client_session_keys),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_kx_server_session_keys", 3,
+ enacl_crypto_kx_server_session_keys),
+ {"crypto_kx_PUBLICKEYBYTES", 0, enacl_crypto_kx_PUBLICKEYBYTES},
+ {"crypto_kx_SECRETKEYBYTES", 0, enacl_crypto_kx_SECRETKEYBYTES},
+ {"crypto_kx_SESSIONKEYBYTES", 0, enacl_crypto_kx_SESSIONKEYBYTES},
- {"crypto_aead_chacha20poly1305_KEYBYTES", 0, enif_crypto_aead_chacha20poly1305_KEYBYTES},
- {"crypto_aead_chacha20poly1305_NPUBBYTES", 0, enif_crypto_aead_chacha20poly1305_NPUBBYTES},
- {"crypto_aead_chacha20poly1305_ABYTES", 0, enif_crypto_aead_chacha20poly1305_ABYTES},
- {"crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX", 0, enif_crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX},
- erl_nif_dirty_job_cpu_bound_macro("crypto_aead_chacha20poly1305_encrypt", 4, enif_crypto_aead_chacha20poly1305_encrypt),
- erl_nif_dirty_job_cpu_bound_macro("crypto_aead_chacha20poly1305_decrypt", 4, enif_crypto_aead_chacha20poly1305_decrypt),
+ {"scramble_block_16", 2, enif_scramble_block_16},
- {"crypto_generichash_BYTES", 0, enif_crypto_generichash_BYTES},
- {"crypto_generichash_BYTES_MIN", 0, enif_crypto_generichash_BYTES_MIN},
- {"crypto_generichash_BYTES_MAX", 0, enif_crypto_generichash_BYTES_MAX},
- {"crypto_generichash_KEYBYTES", 0, enif_crypto_generichash_KEYBYTES},
- {"crypto_generichash_KEYBYTES_MIN", 0, enif_crypto_generichash_KEYBYTES_MIN},
- {"crypto_generichash_KEYBYTES_MAX", 0, enif_crypto_generichash_KEYBYTES_MAX},
- {"crypto_generichash", 3, enif_crypto_generichash},
- {"crypto_generichash_init", 2, enif_crypto_generichash_init},
- {"crypto_generichash_update", 3, enif_crypto_generichash_update},
- {"crypto_generichash_final", 2, enif_crypto_generichash_final}
+ {"crypto_aead_chacha20poly1305_ietf_KEYBYTES", 0,
+ enacl_crypto_aead_chacha20poly1305_ietf_KEYBYTES},
+ {"crypto_aead_chacha20poly1305_ietf_NPUBBYTES", 0,
+ enacl_crypto_aead_chacha20poly1305_ietf_NPUBBYTES},
+ {"crypto_aead_chacha20poly1305_ietf_ABYTES", 0,
+ enacl_crypto_aead_chacha20poly1305_ietf_ABYTES},
+ {"crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX", 0,
+ enacl_crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX},
+ erl_nif_dirty_job_cpu_bound_macro(
+ "crypto_aead_chacha20poly1305_ietf_encrypt", 4,
+ enacl_crypto_aead_chacha20poly1305_ietf_encrypt),
+ erl_nif_dirty_job_cpu_bound_macro(
+ "crypto_aead_chacha20poly1305_ietf_decrypt", 4,
+ enacl_crypto_aead_chacha20poly1305_ietf_decrypt),
-};
+ {"crypto_aead_xchacha20poly1305_ietf_KEYBYTES", 0,
+ enacl_crypto_aead_xchacha20poly1305_ietf_KEYBYTES},
+ {"crypto_aead_xchacha20poly1305_ietf_NPUBBYTES", 0,
+ enacl_crypto_aead_xchacha20poly1305_ietf_NPUBBYTES},
+ {"crypto_aead_xchacha20poly1305_ietf_ABYTES", 0,
+ enacl_crypto_aead_xchacha20poly1305_ietf_ABYTES},
+ {"crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX", 0,
+ enacl_crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX},
+ erl_nif_dirty_job_cpu_bound_macro(
+ "crypto_aead_xchacha20poly1305_ietf_encrypt", 4,
+ enacl_crypto_aead_xchacha20poly1305_ietf_encrypt),
+ erl_nif_dirty_job_cpu_bound_macro(
+ "crypto_aead_xchacha20poly1305_ietf_decrypt", 4,
+ enacl_crypto_aead_xchacha20poly1305_ietf_decrypt),
-ERL_NIF_INIT(enacl_nif, nif_funcs, enif_crypto_load, NULL, NULL, NULL);
+ {"crypto_generichash_BYTES", 0, enacl_crypto_generichash_BYTES},
+ {"crypto_generichash_BYTES_MIN", 0, enacl_crypto_generichash_BYTES_MIN},
+ {"crypto_generichash_BYTES_MAX", 0, enacl_crypto_generichash_BYTES_MAX},
+ {"crypto_generichash_KEYBYTES", 0, enacl_crypto_generichash_KEYBYTES},
+ {"crypto_generichash_KEYBYTES_MIN", 0,
+ enacl_crypto_generichash_KEYBYTES_MIN},
+ {"crypto_generichash_KEYBYTES_MAX", 0,
+ enacl_crypto_generichash_KEYBYTES_MAX},
+ {"crypto_generichash", 3, enacl_crypto_generichash},
+ {"crypto_generichash_init", 2, enacl_crypto_generichash_init},
+ erl_nif_dirty_job_cpu_bound_macro("crypto_generichash_update", 2,
+ enacl_crypto_generichash_update),
+ erl_nif_dirty_job_cpu_bound_macro("crypto_generichash_final", 1,
+ enacl_crypto_generichash_final),
+
+ {"crypto_secretstream_xchacha20poly1305_ABYTES", 0,
+ enacl_crypto_secretstream_xchacha20poly1305_ABYTES},
+ {"crypto_secretstream_xchacha20poly1305_HEADERBYTES", 0,
+ enacl_crypto_secretstream_xchacha20poly1305_HEADERBYTES},
+ {"crypto_secretstream_xchacha20poly1305_KEYBYTES", 0,
+ enacl_crypto_secretstream_xchacha20poly1305_KEYBYTES},
+ {"crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX", 0,
+ enacl_crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX},
+ {"crypto_secretstream_xchacha20poly1305_TAG_MESSAGE", 0,
+ enacl_crypto_secretstream_xchacha20poly1305_TAG_MESSAGE},
+ {"crypto_secretstream_xchacha20poly1305_TAG_PUSH", 0,
+ enacl_crypto_secretstream_xchacha20poly1305_TAG_PUSH},
+ {"crypto_secretstream_xchacha20poly1305_TAG_REKEY", 0,
+ enacl_crypto_secretstream_xchacha20poly1305_TAG_REKEY},
+ {"crypto_secretstream_xchacha20poly1305_TAG_FINAL", 0,
+ enacl_crypto_secretstream_xchacha20poly1305_TAG_FINAL},
+ {"crypto_secretstream_xchacha20poly1305_keygen", 0,
+ enacl_crypto_secretstream_xchacha20poly1305_keygen},
+ {"crypto_secretstream_xchacha20poly1305_init_push", 1,
+ enacl_crypto_secretstream_xchacha20poly1305_init_push},
+ {"crypto_secretstream_xchacha20poly1305_init_pull", 2,
+ enacl_crypto_secretstream_xchacha20poly1305_init_pull},
+ {"crypto_secretstream_xchacha20poly1305_rekey", 1,
+ enacl_crypto_secretstream_xchacha20poly1305_rekey},
+ erl_nif_dirty_job_cpu_bound_macro(
+ "crypto_secretstream_xchacha20poly1305_push", 4,
+ enacl_crypto_secretstream_xchacha20poly1305_push),
+ erl_nif_dirty_job_cpu_bound_macro(
+ "crypto_secretstream_xchacha20poly1305_pull", 3,
+ enacl_crypto_secretstream_xchacha20poly1305_pull)};
+
+ERL_NIF_INIT(enacl_nif, nif_funcs, enacl_crypto_load, NULL, enacl_crypto_upgrade, enacl_crypto_unload);
diff --git a/c_src/generichash.c b/c_src/generichash.c
new file mode 100644
index 0000000..8f184a3
--- /dev/null
+++ b/c_src/generichash.c
@@ -0,0 +1,302 @@
+#include
+
+#include
+
+#include "enacl.h"
+#include "generichash.h"
+
+typedef struct enacl_generichash_ctx {
+ ErlNifMutex *mtx;
+ crypto_generichash_state *ctx; // Underlying hash state from sodium
+ int alive; // Is the context still valid for updates/finalizes?
+ int outlen; // Final size of the hash
+
+} enacl_generichash_ctx;
+
+static ErlNifResourceType *enacl_generic_hash_ctx_rtype;
+
+static void enacl_generic_hash_ctx_dtor(ErlNifEnv *env,
+ enacl_generichash_ctx *);
+
+int enacl_init_generic_hash_ctx(ErlNifEnv *env) {
+ enacl_generic_hash_ctx_rtype =
+ enif_open_resource_type(env, NULL, "enacl_generichash_context",
+ (ErlNifResourceDtor *)enacl_generic_hash_ctx_dtor,
+ ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER, NULL);
+
+ if (enacl_generic_hash_ctx_rtype == NULL)
+ return 0;
+
+ return 1;
+}
+
+static void enacl_generic_hash_ctx_dtor(ErlNifEnv *env,
+ enacl_generichash_ctx *obj) {
+ if (!obj->alive) {
+ return;
+ }
+
+ if (obj->ctx)
+ sodium_free(obj->ctx);
+
+ if (obj->mtx != NULL)
+ enif_mutex_destroy(obj->mtx);
+
+ return;
+}
+
+/*
+ * Generic hash
+ */
+ERL_NIF_TERM enacl_crypto_generichash_BYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_generichash_BYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_generichash_BYTES_MIN(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_generichash_BYTES_MIN);
+}
+
+ERL_NIF_TERM enacl_crypto_generichash_BYTES_MAX(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_generichash_BYTES_MAX);
+}
+
+ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_generichash_KEYBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES_MIN(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_generichash_KEYBYTES_MIN);
+}
+
+ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES_MAX(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_generichash_KEYBYTES_MAX);
+}
+
+ERL_NIF_TERM enacl_crypto_generichash(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary hash, message, key;
+ unsigned hash_size;
+ ERL_NIF_TERM ret;
+
+ // Validate the arguments
+ if (argc != 3)
+ goto bad_arg;
+ if (!enif_get_uint(env, argv[0], &hash_size))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[1], &message))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[2], &key))
+ goto bad_arg;
+
+ // Verify that hash size is
+ // crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX
+ if ((hash_size < crypto_generichash_BYTES_MIN) ||
+ (hash_size > crypto_generichash_BYTES_MAX)) {
+ goto bad_arg;
+ }
+
+ // validate key size
+ unsigned char *k = key.data;
+ if (0 == key.size) {
+ k = NULL;
+ } else if (key.size < crypto_generichash_KEYBYTES_MIN ||
+ key.size > crypto_generichash_KEYBYTES_MAX) {
+ goto bad_arg;
+ }
+
+ // allocate memory for hash
+ if (!enif_alloc_binary(hash_size, &hash)) {
+ goto err;
+ }
+
+ // calculate hash
+ if (0 != crypto_generichash(hash.data, hash.size, message.data, message.size,
+ k, key.size)) {
+ goto release;
+ }
+
+ ret = enif_make_binary(env, &hash);
+ goto done;
+
+bad_arg:
+ return enif_make_badarg(env);
+release:
+ enif_release_binary(&hash);
+err:
+ ret = enacl_internal_error(env);
+done:
+ return ret;
+}
+
+ERL_NIF_TERM enacl_crypto_generichash_init(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary key;
+ unsigned hash_size;
+ enacl_generichash_ctx *obj = NULL;
+ ERL_NIF_TERM ret;
+
+ // Validate the arguments
+ if (argc != 2)
+ goto bad_arg;
+ if (!enif_get_uint(env, argv[0], &hash_size))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[1], &key))
+ goto bad_arg;
+
+ // Verify that hash size is valid
+ if ((hash_size < crypto_generichash_BYTES_MIN) ||
+ (hash_size > crypto_generichash_BYTES_MAX)) {
+ goto bad_arg;
+ }
+
+ // validate key size
+ unsigned char *k = key.data;
+ if (0 == key.size) {
+ k = NULL;
+ } else if (key.size < crypto_generichash_KEYBYTES_MIN ||
+ key.size > crypto_generichash_KEYBYTES_MAX) {
+ goto bad_arg;
+ }
+
+ // Create the resource
+ if ((obj = enif_alloc_resource(enacl_generic_hash_ctx_rtype,
+ sizeof(enacl_generichash_ctx))) == NULL) {
+ goto err;
+ }
+
+ // Allocate the state context via libsodium
+ // Note that this ensures a 64byte alignment for the resource
+ // And also protects the resource via guardpages
+ obj->mtx = NULL;
+ obj->ctx = NULL;
+ obj->alive = 0;
+ obj->outlen = 0;
+
+ obj->ctx = (crypto_generichash_state *)sodium_malloc(
+ crypto_generichash_statebytes());
+ if (obj->ctx == NULL) {
+ goto err;
+ }
+ obj->alive = 1;
+ obj->outlen = hash_size;
+
+ if ((obj->mtx = enif_mutex_create("enacl.generichash")) == NULL) {
+ ret = enacl_error_tuple(env, "mutex_create");
+ goto err;
+ }
+
+ // Call the library function
+ if (0 != crypto_generichash_init(obj->ctx, k, key.size, obj->outlen)) {
+ ret = enacl_error_tuple(env, "hash_init_error");
+ goto err;
+ }
+
+ ret = enif_make_resource(env, obj);
+ goto done;
+
+bad_arg:
+ return enif_make_badarg(env);
+err:
+ ret = enacl_internal_error(env);
+ if (obj != NULL) {
+ if (obj->alive) {
+ sodium_free(obj->ctx);
+ obj->alive = 0; // Maintain the invariant consistently
+ }
+ }
+done:
+ if (obj != NULL) {
+ enif_release_resource(obj);
+ }
+ return ret;
+}
+
+ERL_NIF_TERM enacl_crypto_generichash_update(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ERL_NIF_TERM ret;
+ ErlNifBinary data;
+ unsigned int data_size;
+ enacl_generichash_ctx *obj = NULL;
+
+ // Validate the arguments
+ if (argc != 2)
+ goto bad_arg;
+ if (!enif_get_resource(env, argv[0],
+ (ErlNifResourceType *)enacl_generic_hash_ctx_rtype,
+ (void **)&obj))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[1], &data))
+ goto bad_arg;
+
+ enif_mutex_lock(obj->mtx);
+ if (!obj->alive) {
+ goto err;
+ }
+
+ // Update hash state
+ if (0 != crypto_generichash_update(obj->ctx, data.data, data.size)) {
+ goto err;
+ }
+
+ ret = argv[0];
+ goto done;
+
+bad_arg:
+ return enif_make_badarg(env);
+err:
+ ret = enacl_error_finalized(env);
+done:
+ enif_mutex_unlock(obj->mtx);
+ return ret;
+}
+
+ERL_NIF_TERM enacl_crypto_generichash_final(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ERL_NIF_TERM ret;
+ ErlNifBinary hash;
+ enacl_generichash_ctx *obj = NULL;
+
+ if (argc != 1)
+ goto bad_arg;
+ if (!enif_get_resource(env, argv[0], enacl_generic_hash_ctx_rtype,
+ (void **)&obj))
+ goto bad_arg;
+
+ enif_mutex_lock(obj->mtx);
+ if (!obj->alive) {
+ ret = enacl_error_finalized(env);
+ goto done;
+ }
+
+ if (!enif_alloc_binary(obj->outlen, &hash)) {
+ goto err;
+ }
+
+ if (0 != crypto_generichash_final(obj->ctx, hash.data, hash.size)) {
+ goto release;
+ }
+
+ // Finalize the object such that it cannot be reused by accident
+ if (obj->ctx)
+ sodium_free(obj->ctx);
+ obj->alive = 0;
+
+ ret = enif_make_binary(env, &hash);
+ goto done;
+
+bad_arg:
+ return enif_make_badarg(env);
+release:
+ enif_release_binary(&hash);
+err:
+ ret = enacl_internal_error(env);
+done:
+ enif_mutex_unlock(obj->mtx);
+ return ret;
+}
diff --git a/c_src/generichash.h b/c_src/generichash.h
new file mode 100644
index 0000000..26fb317
--- /dev/null
+++ b/c_src/generichash.h
@@ -0,0 +1,32 @@
+#ifndef ENACL_GENERICHASH_H
+#define ENACL_GENERICHASH_H
+
+#include
+
+int enacl_init_generic_hash_ctx(ErlNifEnv *env);
+
+ERL_NIF_TERM enacl_crypto_generichash_BYTES(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM enacl_crypto_generichash_BYTES_MIN(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM enacl_crypto_generichash_BYTES_MAX(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]);
+
+ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES_MIN(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES_MAX(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]);
+
+ERL_NIF_TERM enacl_crypto_generichash(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]);
+
+ERL_NIF_TERM enacl_crypto_generichash_init(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM enacl_crypto_generichash_update(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM enacl_crypto_generichash_final(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]);
+
+#endif
\ No newline at end of file
diff --git a/c_src/hash.c b/c_src/hash.c
new file mode 100644
index 0000000..84bcded
--- /dev/null
+++ b/c_src/hash.c
@@ -0,0 +1,62 @@
+#include
+
+#include
+
+#include "enacl.h"
+#include "hash.h"
+
+ERL_NIF_TERM enacl_crypto_shorthash_BYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_shorthash_BYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_shorthash_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_shorthash_KEYBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_shorthash(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary a, m, k;
+
+ if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
+ (!enif_inspect_binary(env, argv[1], &k))) {
+ return enif_make_badarg(env);
+ }
+
+ if (k.size != crypto_shorthash_KEYBYTES) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(crypto_shorthash_BYTES, &a)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_shorthash(a.data, m.data, m.size, k.data);
+
+ return enif_make_binary(env, &a);
+}
+
+ERL_NIF_TERM enacl_crypto_hash(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary input;
+ ErlNifBinary result;
+ ERL_NIF_TERM ret;
+
+ if ((argc != 1) || (!enif_inspect_iolist_as_binary(env, argv[0], &input)))
+ goto bad_arg;
+
+ if (!enif_alloc_binary(crypto_hash_BYTES, &result))
+ goto err;
+
+ crypto_hash(result.data, input.data, input.size);
+ ret = enif_make_binary(env, &result);
+ goto done;
+
+bad_arg:
+ return enif_make_badarg(env);
+err:
+ ret = enacl_internal_error(env);
+done:
+ return ret;
+}
diff --git a/c_src/hash.h b/c_src/hash.h
new file mode 100644
index 0000000..080b7bf
--- /dev/null
+++ b/c_src/hash.h
@@ -0,0 +1,17 @@
+#ifndef ENACL_HASH_H
+#define ENACL_HASH_H
+
+#include
+
+ERL_NIF_TERM enacl_crypto_shorthash_BYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_shorthash_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_shorthash(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_hash(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]);
+#endif
\ No newline at end of file
diff --git a/c_src/kdf.c b/c_src/kdf.c
new file mode 100644
index 0000000..5290513
--- /dev/null
+++ b/c_src/kdf.c
@@ -0,0 +1,59 @@
+#include
+
+#include
+
+#include "enacl.h"
+#include "kdf.h"
+
+
+ERL_NIF_TERM enacl_crypto_kdf_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_kdf_KEYBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_kdf_CONTEXTBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_kdf_CONTEXTBYTES);
+}
+
+
+ERL_NIF_TERM enacl_crypto_kdf_derive_from_key(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary m, c, r;
+ uint64_t id;
+
+ // Validate the arguments
+ if ((argc != 3) ||
+ (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
+ (!enif_inspect_binary(env, argv[1], &c)) ||
+ (!enif_get_uint64(env, argv[2], &id))) {
+ return enif_make_badarg(env);
+ }
+
+ // Check Master Key length
+ if (m.size != crypto_kdf_KEYBYTES) {
+ return enif_make_badarg(env);
+ }
+
+ // Check Context Key length
+ if (c.size != crypto_kdf_CONTEXTBYTES) {
+ return enif_make_badarg(env);
+ }
+
+ // Allocate memory for return binary
+ if (!enif_alloc_binary(crypto_kdf_KEYBYTES, &r)) {
+ return enacl_internal_error(env);
+ }
+
+ if (crypto_kdf_derive_from_key(r.data, r.size,
+ id,
+ (const char *)c.data,
+ m.data) != 0) {
+ /* out of memory */
+ enif_release_binary(&r);
+ return enacl_internal_error(env);
+ }
+
+ return enif_make_binary(env, &r);
+}
+
diff --git a/c_src/kdf.h b/c_src/kdf.h
new file mode 100644
index 0000000..56b654b
--- /dev/null
+++ b/c_src/kdf.h
@@ -0,0 +1,15 @@
+#ifndef ENACL_KDF_H
+#define ENACL_KDF_H
+
+#include
+
+ERL_NIF_TERM enacl_crypto_kdf_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_kdf_CONTEXTBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_kdf_derive_from_key(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+#endif
diff --git a/c_src/kx.c b/c_src/kx.c
new file mode 100644
index 0000000..6020e6f
--- /dev/null
+++ b/c_src/kx.c
@@ -0,0 +1,151 @@
+#include
+
+#include
+
+#include "enacl.h"
+#include "kx.h"
+
+/* Key exchange */
+
+ERL_NIF_TERM enacl_crypto_kx_SECRETKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_kx_SECRETKEYBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_kx_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_kx_PUBLICKEYBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_kx_SESSIONKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_kx_SESSIONKEYBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_kx_keypair(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary pk, sk;
+
+ if (argc != 0) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(crypto_kx_PUBLICKEYBYTES, &pk)) {
+ return enacl_internal_error(env);
+ }
+
+ if (!enif_alloc_binary(crypto_kx_SECRETKEYBYTES, &sk)) {
+ enif_release_binary(&pk);
+ return enacl_internal_error(env);
+ }
+
+ crypto_kx_keypair(pk.data, sk.data);
+
+ return enif_make_tuple2(env, enif_make_binary(env, &pk),
+ enif_make_binary(env, &sk));
+}
+
+ERL_NIF_TERM
+enacl_crypto_kx_server_session_keys(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ERL_NIF_TERM ret;
+ ErlNifBinary rx, tx, server_pk, server_sk, client_pk;
+
+ if (argc != 3)
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[0], &server_pk))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[1], &server_sk))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[2], &client_pk))
+ goto bad_arg;
+
+ if (server_pk.size != crypto_kx_PUBLICKEYBYTES)
+ goto bad_arg;
+ if (server_sk.size != crypto_kx_SECRETKEYBYTES)
+ goto bad_arg;
+ if (client_pk.size != crypto_kx_PUBLICKEYBYTES)
+ goto bad_arg;
+
+ if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &rx)) {
+ ret = enacl_internal_error(env);
+ goto done;
+ }
+
+ if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &tx)) {
+ ret = enacl_internal_error(env);
+ goto release_rx;
+ }
+
+ if (0 != crypto_kx_server_session_keys(rx.data, tx.data, server_pk.data,
+ server_sk.data, client_pk.data)) {
+ // suspicious client public key
+ ret = enacl_error_tuple(env, "invalid_client_public_key");
+ goto release_tx_rx;
+ }
+
+ ret = enif_make_tuple2(env, enif_make_binary(env, &rx),
+ enif_make_binary(env, &tx));
+ goto done;
+
+bad_arg:
+ return enif_make_badarg(env);
+release_tx_rx:
+ enif_release_binary(&tx);
+release_rx:
+ enif_release_binary(&rx);
+done:
+ return ret;
+}
+
+ERL_NIF_TERM
+enacl_crypto_kx_client_session_keys(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary rx, tx, client_pk, client_sk, server_pk;
+ ERL_NIF_TERM ret;
+
+ if (argc != 3)
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[0], &client_pk))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[1], &client_sk))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[2], &server_pk))
+ goto bad_arg;
+
+ if (client_pk.size != crypto_kx_PUBLICKEYBYTES)
+ goto bad_arg;
+ if (client_sk.size != crypto_kx_SECRETKEYBYTES)
+ goto bad_arg;
+ if (server_pk.size != crypto_kx_PUBLICKEYBYTES)
+ goto bad_arg;
+
+ if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &rx)) {
+ ret = enacl_internal_error(env);
+ goto done;
+ }
+
+ if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &tx)) {
+ ret = enacl_internal_error(env);
+ goto release_rx;
+ }
+
+ if (0 != crypto_kx_client_session_keys(rx.data, tx.data, client_pk.data,
+ client_sk.data, server_pk.data)) {
+ // suspicious server public key
+ ret = enacl_error_tuple(env, "invalid_server_public_key");
+ goto release_tx_rx;
+ }
+
+ ret = enif_make_tuple2(env, enif_make_binary(env, &rx),
+ enif_make_binary(env, &tx));
+ goto done;
+bad_arg:
+ return enif_make_badarg(env);
+release_tx_rx:
+ enif_release_binary(&tx);
+release_rx:
+ enif_release_binary(&rx);
+done:
+ return ret;
+}
diff --git a/c_src/kx.h b/c_src/kx.h
new file mode 100644
index 0000000..79a7c21
--- /dev/null
+++ b/c_src/kx.h
@@ -0,0 +1,24 @@
+#ifndef ENACL_KX_H
+#define ENACL_KX_H
+
+#include
+
+ERL_NIF_TERM enacl_crypto_kx_SECRETKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_kx_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_kx_SESSIONKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_kx_keypair(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_kx_server_session_keys(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_kx_client_session_keys(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+#endif
diff --git a/c_src/public.c b/c_src/public.c
new file mode 100644
index 0000000..f2113a8
--- /dev/null
+++ b/c_src/public.c
@@ -0,0 +1,286 @@
+#include
+
+#include
+
+#include "enacl.h"
+#include "public.h"
+
+/* Public-key cryptography */
+ERL_NIF_TERM enacl_crypto_box_NONCEBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_box_NONCEBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_box_ZEROBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_box_ZEROBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_box_BOXZEROBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_box_BOXZEROBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_box_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_box_PUBLICKEYBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_box_SECRETKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_box_SECRETKEYBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_box_BEFORENMBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_box_BEFORENMBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_box_SEALBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_box_SEALBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_box_keypair(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary pk, sk;
+
+ if (argc != 0) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(crypto_box_PUBLICKEYBYTES, &pk)) {
+ return enacl_error_tuple(env, "alloc_failed");
+ }
+
+ if (!enif_alloc_binary(crypto_box_SECRETKEYBYTES, &sk)) {
+ enif_release_binary(&pk);
+ return enacl_error_tuple(env, "alloc_failed");
+ }
+
+ crypto_box_keypair(pk.data, sk.data);
+
+ return enif_make_tuple2(env, enif_make_binary(env, &pk),
+ enif_make_binary(env, &sk));
+}
+
+ERL_NIF_TERM enacl_crypto_box(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary padded_msg, nonce, pk, sk, result;
+ ERL_NIF_TERM ret;
+
+ if (argc != 4)
+ goto bad_arg;
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &padded_msg))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[1], &nonce))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[2], &pk))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[3], &sk))
+ goto bad_arg;
+
+ if (nonce.size != crypto_box_NONCEBYTES)
+ goto bad_arg;
+ if (pk.size != crypto_box_PUBLICKEYBYTES)
+ goto bad_arg;
+ if (sk.size != crypto_box_SECRETKEYBYTES)
+ goto bad_arg;
+ if (padded_msg.size < crypto_box_ZEROBYTES)
+ goto bad_arg;
+
+ if (!enif_alloc_binary(padded_msg.size, &result)) {
+ goto done;
+ }
+
+ if (0 != crypto_box(result.data, padded_msg.data, padded_msg.size, nonce.data,
+ pk.data, sk.data)) {
+ goto release;
+ }
+
+ ret = enif_make_sub_binary(env, enif_make_binary(env, &result),
+ crypto_box_BOXZEROBYTES,
+ padded_msg.size - crypto_box_BOXZEROBYTES);
+
+ goto done;
+
+bad_arg:
+ return enif_make_badarg(env);
+release:
+ enif_release_binary(&result);
+err:
+ ret = enacl_internal_error(env);
+done:
+ return ret;
+}
+
+ERL_NIF_TERM enacl_crypto_box_open(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary padded_ciphertext, nonce, pk, sk, result;
+
+ if ((argc != 4) ||
+ (!enif_inspect_iolist_as_binary(env, argv[0], &padded_ciphertext)) ||
+ (!enif_inspect_binary(env, argv[1], &nonce)) ||
+ (!enif_inspect_binary(env, argv[2], &pk)) ||
+ (!enif_inspect_binary(env, argv[3], &sk))) {
+ return enif_make_badarg(env);
+ }
+
+ if ((nonce.size != crypto_box_NONCEBYTES) ||
+ (pk.size != crypto_box_PUBLICKEYBYTES) ||
+ (sk.size != crypto_box_SECRETKEYBYTES) ||
+ (padded_ciphertext.size < crypto_box_BOXZEROBYTES)) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(padded_ciphertext.size, &result)) {
+ return enacl_internal_error(env);
+ }
+
+ if (0 != crypto_box_open(result.data, padded_ciphertext.data,
+ padded_ciphertext.size, nonce.data, pk.data,
+ sk.data)) {
+ enif_release_binary(&result);
+ return enacl_error_tuple(env, "failed_verification");
+ }
+
+ ERL_NIF_TERM ret_ok = enif_make_atom(env, ATOM_OK);
+ ERL_NIF_TERM ret_bin = enif_make_sub_binary(
+ env, enif_make_binary(env, &result), crypto_box_ZEROBYTES,
+ padded_ciphertext.size - crypto_box_ZEROBYTES);
+
+ return enif_make_tuple2(env, ret_ok, ret_bin);
+}
+
+/* Precomputed crypto boxes */
+
+ERL_NIF_TERM enacl_crypto_box_beforenm(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary k, pk, sk;
+
+ if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &pk)) ||
+ (!enif_inspect_binary(env, argv[1], &sk)) ||
+ (pk.size != crypto_box_PUBLICKEYBYTES) ||
+ (sk.size != crypto_box_SECRETKEYBYTES)) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(crypto_box_BEFORENMBYTES, &k)) {
+ goto err;
+ }
+
+ if (0 != crypto_box_beforenm(k.data, pk.data, sk.data)) {
+ // error
+ enif_release_binary(&k);
+ goto err;
+ }
+
+ return enif_make_binary(env, &k);
+err:
+ return enacl_internal_error(env);
+}
+
+ERL_NIF_TERM enacl_crypto_box_afternm(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary result, m, nonce, k;
+
+ if ((argc != 3) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
+ (!enif_inspect_binary(env, argv[1], &nonce)) ||
+ (!enif_inspect_binary(env, argv[2], &k)) ||
+ (m.size < crypto_box_ZEROBYTES) ||
+ (nonce.size != crypto_box_NONCEBYTES) ||
+ (k.size != crypto_box_BEFORENMBYTES)) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(m.size, &result)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_box_afternm(result.data, m.data, m.size, nonce.data, k.data);
+
+ return enif_make_sub_binary(env, enif_make_binary(env, &result),
+ crypto_box_BOXZEROBYTES,
+ m.size - crypto_box_BOXZEROBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_box_open_afternm(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary result, m, nonce, k;
+
+ if ((argc != 3) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
+ (!enif_inspect_binary(env, argv[1], &nonce)) ||
+ (!enif_inspect_binary(env, argv[2], &k)) ||
+ (m.size < crypto_box_BOXZEROBYTES) ||
+ (nonce.size != crypto_box_NONCEBYTES) ||
+ (k.size != crypto_box_BEFORENMBYTES)) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(m.size, &result)) {
+ return enacl_internal_error(env);
+ }
+
+ if (0 != crypto_box_open_afternm(result.data, m.data, m.size, nonce.data,
+ k.data)) {
+ enif_release_binary(&result);
+ return enacl_error_tuple(env, "failed_verification");
+ }
+
+ ERL_NIF_TERM ret_ok = enif_make_atom(env, ATOM_OK);
+ ERL_NIF_TERM ret_bin =
+ enif_make_sub_binary(env, enif_make_binary(env, &result),
+ crypto_box_ZEROBYTES, m.size - crypto_box_ZEROBYTES);
+ return enif_make_tuple2(env, ret_ok, ret_bin);
+}
+
+/* Sealed box functions */
+
+ERL_NIF_TERM enacl_crypto_box_seal(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary key, msg, ciphertext;
+
+ if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &msg)) ||
+ (!enif_inspect_binary(env, argv[1], &key))) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(msg.size + crypto_box_SEALBYTES, &ciphertext)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_box_seal(ciphertext.data, msg.data, msg.size, key.data);
+
+ return enif_make_binary(env, &ciphertext);
+}
+
+ERL_NIF_TERM enacl_crypto_box_seal_open(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary pk, sk, ciphertext, msg;
+
+ if ((argc != 3) ||
+ (!enif_inspect_iolist_as_binary(env, argv[0], &ciphertext)) ||
+ (!enif_inspect_binary(env, argv[1], &pk)) ||
+ (!enif_inspect_binary(env, argv[2], &sk))) {
+ return enif_make_badarg(env);
+ }
+
+ if (ciphertext.size < crypto_box_SEALBYTES) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(ciphertext.size - crypto_box_SEALBYTES, &msg)) {
+ return enacl_internal_error(env);
+ }
+
+ if (crypto_box_seal_open(msg.data, ciphertext.data, ciphertext.size, pk.data,
+ sk.data) != 0) {
+ enif_release_binary(&msg);
+ return enacl_error_tuple(env, "failed_verification");
+ }
+
+ ERL_NIF_TERM ret_ok = enif_make_atom(env, ATOM_OK);
+ ERL_NIF_TERM ret_bin = enif_make_binary(env, &msg);
+
+ return enif_make_tuple2(env, ret_ok, ret_bin);
+}
diff --git a/c_src/public.h b/c_src/public.h
new file mode 100644
index 0000000..b750eb1
--- /dev/null
+++ b/c_src/public.h
@@ -0,0 +1,49 @@
+#ifndef ENACL_PUBLIC_H
+#define ENACL_PUBLIC_H
+
+ERL_NIF_TERM enacl_crypto_box_NONCEBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_box_ZEROBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_box_BOXZEROBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_box_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_box_SECRETKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_box_BEFORENMBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_box_keypair(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_box(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_box_open(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_box_beforenm(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_box_afternm(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_box_open_afternm(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_box_SEALBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_box_seal(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_box_seal_open(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+#endif
\ No newline at end of file
diff --git a/c_src/pwhash.c b/c_src/pwhash.c
new file mode 100644
index 0000000..fc49c91
--- /dev/null
+++ b/c_src/pwhash.c
@@ -0,0 +1,180 @@
+#include
+
+#include
+
+#include "enacl.h"
+#include "pwhash.h"
+
+static size_t enacl_pwhash_opslimit(ErlNifEnv *env, ERL_NIF_TERM arg) {
+ ERL_NIF_TERM a;
+ size_t r;
+
+ if (enif_is_atom(env, arg)) {
+ a = enif_make_atom(env, "interactive");
+ if (enif_is_identical(a, arg)) {
+ return crypto_pwhash_OPSLIMIT_INTERACTIVE;
+ }
+
+ a = enif_make_atom(env, "moderate");
+ if (enif_is_identical(a, arg)) {
+ return crypto_pwhash_OPSLIMIT_MODERATE;
+ }
+
+ a = enif_make_atom(env, "sensitive");
+ if (enif_is_identical(a, arg)) {
+ return crypto_pwhash_OPSLIMIT_SENSITIVE;
+ }
+ } else if (enif_get_ulong(env, arg, &r)) {
+ return r;
+ }
+
+ return 0;
+}
+
+static size_t enacl_pwhash_memlimit(ErlNifEnv *env, ERL_NIF_TERM arg) {
+ ERL_NIF_TERM a;
+ size_t r;
+
+ if (enif_is_atom(env, arg)) {
+ a = enif_make_atom(env, "interactive");
+ if (enif_is_identical(a, arg)) {
+ return crypto_pwhash_MEMLIMIT_INTERACTIVE;
+ }
+
+ a = enif_make_atom(env, "moderate");
+ if (enif_is_identical(a, arg)) {
+ return crypto_pwhash_MEMLIMIT_MODERATE;
+ }
+
+ a = enif_make_atom(env, "sensitive");
+ if (enif_is_identical(a, arg)) {
+ return crypto_pwhash_MEMLIMIT_SENSITIVE;
+ }
+ } else if (enif_get_ulong(env, arg, &r)) {
+ return r;
+ }
+
+ return 0;
+}
+
+ERL_NIF_TERM enacl_crypto_pwhash_SALTBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_pwhash_SALTBYTES);
+}
+
+static int enacl_pwhash_alg(ErlNifEnv *env, ERL_NIF_TERM arg) {
+ ERL_NIF_TERM a;
+ int r;
+
+ if (enif_is_atom(env, arg)) {
+ a = enif_make_atom(env, "default");
+ if (enif_is_identical(a, arg)) {
+ return crypto_pwhash_ALG_DEFAULT;
+ }
+
+ a = enif_make_atom(env, "argon2i13");
+ if (enif_is_identical(a, arg)) {
+ return crypto_pwhash_ALG_ARGON2I13;
+ }
+
+ a = enif_make_atom(env, "argon2id13");
+ if (enif_is_identical(a, arg)) {
+ return crypto_pwhash_ALG_ARGON2ID13;
+ }
+ } else if (enif_get_int(env, arg, &r)) {
+ return r;
+ }
+
+ return 0;
+}
+
+ERL_NIF_TERM enacl_crypto_pwhash(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary h, p, s;
+ size_t o, m;
+ int alg;
+
+ // Validate the arguments
+ if ((argc != 5) || (!enif_inspect_iolist_as_binary(env, argv[0], &p)) ||
+ (!enif_inspect_binary(env, argv[1], &s)) ||
+ !(o = enacl_pwhash_opslimit(env, argv[2])) ||
+ !(m = enacl_pwhash_memlimit(env, argv[3])) ||
+ !(alg = enacl_pwhash_alg(env, argv[4]))) {
+ return enif_make_badarg(env);
+ }
+
+ // Check limits
+ if ((o < crypto_pwhash_OPSLIMIT_MIN) || (o > crypto_pwhash_OPSLIMIT_MAX) ||
+ (m < crypto_pwhash_MEMLIMIT_MIN) || (m > crypto_pwhash_MEMLIMIT_MAX)) {
+ return enif_make_badarg(env);
+ }
+
+ // Check Salt size
+ if (s.size != crypto_pwhash_SALTBYTES) {
+ return enif_make_badarg(env);
+ }
+
+ // Allocate memory for return binary
+ if (!enif_alloc_binary(crypto_box_SEEDBYTES, &h)) {
+ return enacl_internal_error(env);
+ }
+
+ if (crypto_pwhash(h.data, h.size, (char *)p.data, p.size, s.data, o, m,
+ alg) != 0) {
+ /* out of memory */
+ enif_release_binary(&h);
+ return enacl_internal_error(env);
+ }
+
+ return enif_make_binary(env, &h);
+}
+
+ERL_NIF_TERM enacl_crypto_pwhash_str(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary h, p;
+ size_t o, m;
+
+ // Validate the arguments
+ if ((argc != 3) || (!enif_inspect_iolist_as_binary(env, argv[0], &p)) ||
+ !(o = enacl_pwhash_opslimit(env, argv[1])) ||
+ !(m = enacl_pwhash_memlimit(env, argv[2]))) {
+ return enif_make_badarg(env);
+ }
+
+ // Check limits
+ if ((o < crypto_pwhash_OPSLIMIT_MIN) || (o > crypto_pwhash_OPSLIMIT_MAX) ||
+ (m < crypto_pwhash_MEMLIMIT_MIN) || (m > crypto_pwhash_MEMLIMIT_MAX)) {
+ return enif_make_badarg(env);
+ }
+
+ // Allocate memory for return binary
+ if (!enif_alloc_binary(crypto_pwhash_STRBYTES, &h)) {
+ return enacl_internal_error(env);
+ }
+
+ if (crypto_pwhash_str((char *)h.data, (char *)p.data, p.size, o, m) != 0) {
+ /* out of memory */
+ enif_release_binary(&h);
+ return enacl_internal_error(env);
+ }
+
+ return enif_make_binary(env, &h);
+}
+
+ERL_NIF_TERM enacl_crypto_pwhash_str_verify(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary h, p;
+ // Validate the arguments
+ if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &h)) ||
+ (!enif_inspect_iolist_as_binary(env, argv[1], &p))) {
+ return enif_make_badarg(env);
+ }
+
+ ERL_NIF_TERM ret = enif_make_atom(env, ATOM_TRUE);
+ if (crypto_pwhash_str_verify((char *)h.data, (char *)p.data, p.size) != 0) {
+ /* wrong password */
+ ret = enif_make_atom(env, ATOM_FALSE);
+ }
+
+ return ret;
+}
diff --git a/c_src/pwhash.h b/c_src/pwhash.h
new file mode 100644
index 0000000..66d0e89
--- /dev/null
+++ b/c_src/pwhash.h
@@ -0,0 +1,18 @@
+#ifndef ENACL_PWHASH_H
+#define ENACL_PWHASH_H
+
+#include
+
+ERL_NIF_TERM enacl_crypto_pwhash_SALTBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_pwhash(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_pwhash_str(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_pwhash_str_verify(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+#endif
diff --git a/c_src/randombytes.c b/c_src/randombytes.c
new file mode 100644
index 0000000..209f5b7
--- /dev/null
+++ b/c_src/randombytes.c
@@ -0,0 +1,49 @@
+#include
+
+#include
+
+#include "enacl.h"
+#include "randombytes.h"
+
+ERL_NIF_TERM enif_randombytes(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ unsigned req_size;
+ ErlNifBinary result;
+
+ if ((argc != 1) || (!enif_get_uint(env, argv[0], &req_size))) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(req_size, &result)) {
+ return enacl_internal_error(env);
+ }
+
+ randombytes(result.data, result.size);
+
+ return enif_make_binary(env, &result);
+}
+
+ERL_NIF_TERM enif_randombytes_uint32(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifUInt64 result;
+
+ if (argc != 0) {
+ return enif_make_badarg(env);
+ }
+
+ result = randombytes_random();
+ return enif_make_uint64(env, result);
+}
+
+ERL_NIF_TERM enif_randombytes_uniform(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ unsigned upper_bound;
+ ErlNifUInt64 result;
+
+ if ((argc != 1) || (!enif_get_uint(env, argv[0], &upper_bound))) {
+ return enif_make_badarg(env);
+ }
+
+ result = randombytes_uniform(upper_bound);
+ return enif_make_uint64(env, result);
+}
diff --git a/c_src/randombytes.h b/c_src/randombytes.h
new file mode 100644
index 0000000..bc8cedf
--- /dev/null
+++ b/c_src/randombytes.h
@@ -0,0 +1,15 @@
+#ifndef ENACL_RANDOMBYTES_H
+#define ENACL_RANDOMBYTES_H
+
+#include
+
+ERL_NIF_TERM enif_randombytes(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enif_randombytes_uint32(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enif_randombytes_uniform(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+#endif
\ No newline at end of file
diff --git a/c_src/secret.c b/c_src/secret.c
new file mode 100644
index 0000000..627d444
--- /dev/null
+++ b/c_src/secret.c
@@ -0,0 +1,324 @@
+#include
+
+#include
+
+#include "enacl.h"
+#include "secret.h"
+
+/* Secret key cryptography */
+
+ERL_NIF_TERM
+enacl_crypto_secretbox_NONCEBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_secretbox_NONCEBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_secretbox_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_secretbox_KEYBYTES);
+}
+
+ERL_NIF_TERM
+enacl_crypto_secretbox_ZEROBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_secretbox_ZEROBYTES);
+}
+
+ERL_NIF_TERM
+enacl_crypto_secretbox_BOXZEROBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_secretbox_BOXZEROBYTES);
+}
+
+ERL_NIF_TERM
+enacl_crypto_stream_chacha20_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_stream_chacha20_KEYBYTES);
+}
+
+ERL_NIF_TERM
+enacl_crypto_stream_chacha20_NONCEBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_stream_chacha20_NONCEBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_stream_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_stream_KEYBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_stream_NONCEBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_stream_NONCEBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_auth_BYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_auth_BYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_auth_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_auth_KEYBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_onetimeauth_BYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_onetimeauth_BYTES);
+}
+
+ERL_NIF_TERM
+enacl_crypto_onetimeauth_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_onetimeauth_KEYBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_secretbox(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary key, nonce, padded_msg, padded_ciphertext;
+
+ if ((argc != 3) ||
+ (!enif_inspect_iolist_as_binary(env, argv[0], &padded_msg)) ||
+ (!enif_inspect_binary(env, argv[1], &nonce)) ||
+ (!enif_inspect_binary(env, argv[2], &key))) {
+ return enif_make_badarg(env);
+ }
+
+ if ((key.size != crypto_secretbox_KEYBYTES) ||
+ (nonce.size != crypto_secretbox_NONCEBYTES) ||
+ (padded_msg.size < crypto_secretbox_ZEROBYTES)) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(padded_msg.size, &padded_ciphertext)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_secretbox(padded_ciphertext.data, padded_msg.data, padded_msg.size,
+ nonce.data, key.data);
+
+ return enif_make_sub_binary(env, enif_make_binary(env, &padded_ciphertext),
+ crypto_secretbox_BOXZEROBYTES,
+ padded_msg.size - crypto_secretbox_BOXZEROBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_secretbox_open(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary key, nonce, padded_ciphertext, padded_msg;
+
+ if ((argc != 3) ||
+ (!enif_inspect_iolist_as_binary(env, argv[0], &padded_ciphertext)) ||
+ (!enif_inspect_binary(env, argv[1], &nonce)) ||
+ (!enif_inspect_binary(env, argv[2], &key))) {
+ return enif_make_badarg(env);
+ }
+
+ if ((key.size != crypto_secretbox_KEYBYTES) ||
+ (nonce.size != crypto_secretbox_NONCEBYTES) ||
+ (padded_ciphertext.size < crypto_secretbox_BOXZEROBYTES)) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(padded_ciphertext.size, &padded_msg)) {
+ return enacl_internal_error(env);
+ }
+
+ if (crypto_secretbox_open(padded_msg.data, padded_ciphertext.data,
+ padded_ciphertext.size, nonce.data,
+ key.data) != 0) {
+ enif_release_binary(&padded_msg);
+ return enacl_error_tuple(env, "failed_verification");
+ }
+
+ ERL_NIF_TERM ret_ok = enif_make_atom(env, ATOM_OK);
+ ERL_NIF_TERM ret_bin = enif_make_sub_binary(
+ env, enif_make_binary(env, &padded_msg), crypto_secretbox_ZEROBYTES,
+ padded_ciphertext.size - crypto_secretbox_ZEROBYTES);
+ return enif_make_tuple2(env, ret_ok, ret_bin);
+}
+
+ERL_NIF_TERM enacl_crypto_stream_chacha20(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary c, n, k;
+ ErlNifUInt64 clen;
+
+ if ((argc != 3) || (!enif_get_uint64(env, argv[0], &clen)) ||
+ (!enif_inspect_binary(env, argv[1], &n)) ||
+ (!enif_inspect_binary(env, argv[2], &k))) {
+ return enif_make_badarg(env);
+ }
+
+ if ((k.size != crypto_stream_chacha20_KEYBYTES) ||
+ (n.size != crypto_stream_chacha20_NONCEBYTES)) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(clen, &c)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_stream_chacha20(c.data, c.size, n.data, k.data);
+
+ return enif_make_binary(env, &c);
+}
+
+ERL_NIF_TERM
+enacl_crypto_stream_chacha20_xor(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary c, m, n, k;
+
+ if ((argc != 3) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
+ (!enif_inspect_binary(env, argv[1], &n)) ||
+ (!enif_inspect_binary(env, argv[2], &k))) {
+ return enif_make_badarg(env);
+ }
+
+ if ((k.size != crypto_stream_chacha20_KEYBYTES) ||
+ (n.size != crypto_stream_chacha20_NONCEBYTES)) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(m.size, &c)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_stream_chacha20_xor(c.data, m.data, m.size, n.data, k.data);
+
+ return enif_make_binary(env, &c);
+}
+
+ERL_NIF_TERM enacl_crypto_stream(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary c, n, k;
+ ErlNifUInt64 clen;
+
+ if ((argc != 3) || (!enif_get_uint64(env, argv[0], &clen)) ||
+ (!enif_inspect_binary(env, argv[1], &n)) ||
+ (!enif_inspect_binary(env, argv[2], &k))) {
+ return enif_make_badarg(env);
+ }
+
+ if ((k.size != crypto_stream_KEYBYTES) ||
+ (n.size != crypto_stream_NONCEBYTES)) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(clen, &c)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_stream(c.data, c.size, n.data, k.data);
+
+ return enif_make_binary(env, &c);
+}
+
+ERL_NIF_TERM enacl_crypto_stream_xor(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary c, m, n, k;
+
+ if ((argc != 3) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
+ (!enif_inspect_binary(env, argv[1], &n)) ||
+ (!enif_inspect_binary(env, argv[2], &k))) {
+ return enif_make_badarg(env);
+ }
+
+ if ((k.size != crypto_stream_KEYBYTES) ||
+ (n.size != crypto_stream_NONCEBYTES)) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(m.size, &c)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_stream_xor(c.data, m.data, m.size, n.data, k.data);
+
+ return enif_make_binary(env, &c);
+}
+
+ERL_NIF_TERM enacl_crypto_auth(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary a, m, k;
+
+ if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
+ (!enif_inspect_binary(env, argv[1], &k))) {
+ return enif_make_badarg(env);
+ }
+
+ if (k.size != crypto_auth_KEYBYTES) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(crypto_auth_BYTES, &a)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_auth(a.data, m.data, m.size, k.data);
+
+ return enif_make_binary(env, &a);
+}
+
+ERL_NIF_TERM enacl_crypto_auth_verify(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary a, m, k;
+
+ if ((argc != 3) || (!enif_inspect_binary(env, argv[0], &a)) ||
+ (!enif_inspect_iolist_as_binary(env, argv[1], &m)) ||
+ (!enif_inspect_binary(env, argv[2], &k))) {
+ return enif_make_badarg(env);
+ }
+
+ if ((k.size != crypto_auth_KEYBYTES) || (a.size != crypto_auth_BYTES)) {
+ return enif_make_badarg(env);
+ }
+
+ if (0 == crypto_auth_verify(a.data, m.data, m.size, k.data)) {
+ return enif_make_atom(env, "true");
+ } else {
+ return enif_make_atom(env, "false");
+ }
+}
+
+ERL_NIF_TERM enacl_crypto_onetimeauth(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary a, m, k;
+
+ if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
+ (!enif_inspect_binary(env, argv[1], &k))) {
+ return enif_make_badarg(env);
+ }
+
+ if (k.size != crypto_onetimeauth_KEYBYTES) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(crypto_onetimeauth_BYTES, &a)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_onetimeauth(a.data, m.data, m.size, k.data);
+
+ return enif_make_binary(env, &a);
+}
+
+ERL_NIF_TERM enacl_crypto_onetimeauth_verify(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary a, m, k;
+
+ if ((argc != 3) || (!enif_inspect_binary(env, argv[0], &a)) ||
+ (!enif_inspect_iolist_as_binary(env, argv[1], &m)) ||
+ (!enif_inspect_binary(env, argv[2], &k))) {
+ return enif_make_badarg(env);
+ }
+
+ if ((k.size != crypto_onetimeauth_KEYBYTES) ||
+ (a.size != crypto_onetimeauth_BYTES)) {
+ return enif_make_badarg(env);
+ }
+
+ if (0 == crypto_onetimeauth_verify(a.data, m.data, m.size, k.data)) {
+ return enif_make_atom(env, "true");
+ } else {
+ return enif_make_atom(env, "false");
+ }
+}
diff --git a/c_src/secret.h b/c_src/secret.h
new file mode 100644
index 0000000..6c0a4c8
--- /dev/null
+++ b/c_src/secret.h
@@ -0,0 +1,70 @@
+#ifndef ENACL_SECRET_H
+#define ENACL_SECRET_H
+
+ERL_NIF_TERM enacl_crypto_secretbox_NONCEBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_secretbox_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_secretbox_ZEROBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_secretbox_BOXZEROBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_stream_chacha20_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_stream_chacha20_NONCEBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_stream_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_stream_NONCEBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_auth_BYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_auth_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_onetimeauth_BYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_onetimeauth_KEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_secretbox(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_secretbox_open(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_stream_chacha20(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_stream_chacha20_xor(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_stream(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_stream_xor(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_auth(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_auth_verify(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_onetimeauth(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_onetimeauth_verify(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+#endif
diff --git a/c_src/secretstream.c b/c_src/secretstream.c
new file mode 100644
index 0000000..51414e7
--- /dev/null
+++ b/c_src/secretstream.c
@@ -0,0 +1,460 @@
+#include
+#include
+
+#include "enacl.h"
+#include "secretstream.h"
+
+typedef struct enacl_secretstream_ctx {
+ ErlNifMutex *mtx;
+ crypto_secretstream_xchacha20poly1305_state
+ *state; // The underlying secretstream state
+ int alive; // Is the context still valid for updates/finalization
+} enacl_secretstream_ctx;
+
+ErlNifResourceType *enacl_secretstream_ctx_rtype = NULL;
+
+static void enacl_secretstream_ctx_dtor(ErlNifEnv *env,
+ enacl_secretstream_ctx *);
+
+int enacl_init_secretstream_ctx(ErlNifEnv *env) {
+ enacl_secretstream_ctx_rtype =
+ enif_open_resource_type(env, NULL, "enacl_secretstream_context",
+ (ErlNifResourceDtor *)enacl_secretstream_ctx_dtor,
+ ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER, NULL);
+
+ if (enacl_secretstream_ctx_rtype == NULL)
+ return 0;
+
+ return 1;
+}
+
+static void enacl_secretstream_ctx_dtor(ErlNifEnv *env,
+ enacl_secretstream_ctx *obj) {
+ if (!obj->alive) {
+ return;
+ }
+
+ if (obj->state)
+ sodium_memzero(obj->state,
+ crypto_secretstream_xchacha20poly1305_statebytes());
+ enif_free(obj->state);
+
+ if (obj->mtx != NULL)
+ enif_mutex_destroy(obj->mtx);
+
+ return;
+}
+
+/*
+ * Secretstream
+ */
+
+ERL_NIF_TERM
+enacl_crypto_secretstream_xchacha20poly1305_ABYTES(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]) {
+ return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_ABYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_HEADERBYTES(
+ ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ return enif_make_int64(env,
+ crypto_secretstream_xchacha20poly1305_HEADERBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_KEYBYTES(
+ ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_KEYBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX(
+ ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ return enif_make_int64(
+ env, crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX);
+}
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_MESSAGE(
+ ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+
+ return enif_make_int64(env,
+ crypto_secretstream_xchacha20poly1305_TAG_MESSAGE);
+}
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_PUSH(
+ ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+
+ return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_TAG_PUSH);
+}
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_REKEY(
+ ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+
+ return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_TAG_REKEY);
+}
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_FINAL(
+ ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+
+ return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_TAG_FINAL);
+}
+
+ERL_NIF_TERM
+enacl_crypto_secretstream_xchacha20poly1305_keygen(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]) {
+
+ ErlNifBinary key;
+
+ if (argc != 0) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(crypto_secretstream_xchacha20poly1305_KEYBYTES,
+ &key)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_secretstream_xchacha20poly1305_keygen(key.data);
+
+ return enif_make_binary(env, &key);
+}
+
+/*
+ int crypto_secretstream_xchacha20poly1305_init_push
+ (crypto_secretstream_xchacha20poly1305_state *state,
+ unsigned char out[crypto_secretstream_xchacha20poly1305_HEADERBYTES],
+ const unsigned char k[crypto_secretstream_xchacha20poly1305_KEYBYTES])
+*/
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_init_push(
+ ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ ERL_NIF_TERM ret;
+ ErlNifBinary key, header;
+ enacl_secretstream_ctx *obj = NULL;
+
+ if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &key))) {
+ goto bad_arg;
+ }
+
+ if (key.size != crypto_secretstream_xchacha20poly1305_KEYBYTES) {
+ goto bad_arg;
+ }
+
+ if (!enif_alloc_binary(crypto_secretstream_xchacha20poly1305_HEADERBYTES,
+ &header)) {
+ ret = enacl_internal_error(env);
+ goto done;
+ }
+
+ if ((obj = enif_alloc_resource(enacl_secretstream_ctx_rtype,
+ sizeof(enacl_secretstream_ctx))) == NULL) {
+ ret = enacl_internal_error(env);
+ goto release_header;
+ }
+ obj->alive = 0;
+ obj->state = enif_alloc(crypto_secretstream_xchacha20poly1305_statebytes());
+
+ if (obj->state == NULL) {
+ ret = enacl_internal_error(env);
+ goto release;
+ }
+ obj->alive = 1;
+
+ if ((obj->mtx = enif_mutex_create("enacl.secretstream")) == NULL) {
+ ret = enacl_internal_error(env);
+ goto free;
+ }
+
+ crypto_secretstream_xchacha20poly1305_init_push(obj->state, header.data,
+ key.data);
+
+ ret = enif_make_tuple2(env, enif_make_binary(env, &header),
+ enif_make_resource(env, obj));
+
+ goto release;
+bad_arg:
+ return enif_make_badarg(env);
+free:
+ if (obj->alive)
+ if (obj->state != NULL) {
+ sodium_memzero(obj->state,
+ crypto_secretstream_xchacha20poly1305_statebytes());
+ enif_free(obj->state);
+ obj->state = NULL;
+ }
+release_header:
+ enif_release_binary(&header);
+release:
+ // This also frees the mutex via the destructor
+ enif_release_resource(obj);
+done:
+ return ret;
+}
+
+/*
+crypto_secretstream_xchacha20poly1305_init_pull
+ (crypto_secretstream_xchacha20poly1305_state *state,
+ const unsigned char in[crypto_secretstream_xchacha20poly1305_HEADERBYTES],
+ const unsigned char k[crypto_secretstream_xchacha20poly1305_KEYBYTES])
+*/
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_init_pull(
+ ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ ERL_NIF_TERM ret;
+ ErlNifBinary header, key;
+ enacl_secretstream_ctx *obj = NULL;
+
+ if (argc != 2) {
+ goto bad_arg;
+ }
+
+ if (!enif_inspect_binary(env, argv[0], &header)) {
+ goto bad_arg;
+ }
+
+ if (!enif_inspect_binary(env, argv[1], &key)) {
+ goto bad_arg;
+ }
+
+ if ((key.size != crypto_secretstream_xchacha20poly1305_KEYBYTES) ||
+ (header.size != crypto_secretstream_xchacha20poly1305_HEADERBYTES)) {
+ goto bad_arg;
+ }
+
+ if ((obj = enif_alloc_resource(enacl_secretstream_ctx_rtype,
+ sizeof(enacl_secretstream_ctx))) == NULL) {
+ ret = enacl_internal_error(env);
+ goto done;
+ }
+
+ obj->alive = 0;
+ obj->state = enif_alloc(crypto_secretstream_xchacha20poly1305_statebytes());
+
+ if (obj->state == NULL) {
+ goto release;
+ }
+ obj->alive = 1;
+
+ if ((obj->mtx = enif_mutex_create("enacl.secretstream")) == NULL) {
+ goto free;
+ }
+
+ crypto_secretstream_xchacha20poly1305_init_pull(obj->state, header.data,
+ key.data);
+
+ ret = enif_make_resource(env, obj);
+
+ goto release;
+
+bad_arg:
+ return enif_make_badarg(env);
+free:
+ if (obj->alive)
+ if (obj->state != NULL) {
+ sodium_memzero(obj->state,
+ crypto_secretstream_xchacha20poly1305_statebytes());
+ enif_free(obj->state);
+ obj->state = NULL;
+ }
+release:
+ // This also frees the mutex via the destructor
+ enif_release_resource(obj);
+done:
+ return ret;
+}
+
+/*
+void
+crypto_secretstream_xchacha20poly1305_rekey
+ (crypto_secretstream_xchacha20poly1305_state *state)
+*/
+ERL_NIF_TERM
+enacl_crypto_secretstream_xchacha20poly1305_rekey(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]) {
+ ERL_NIF_TERM ret;
+ enacl_secretstream_ctx *obj = NULL;
+
+ if (argc != 1) {
+ goto bad_arg;
+ }
+
+ if (!enif_get_resource(env, argv[0],
+ (ErlNifResourceType *)enacl_secretstream_ctx_rtype,
+ (void **)&obj)) {
+ goto bad_arg;
+ }
+
+ enif_mutex_lock(obj->mtx);
+ if (!obj->alive) {
+ goto err;
+ }
+
+ crypto_secretstream_xchacha20poly1305_rekey(obj->state);
+
+ ret = enif_make_atom(env, ATOM_OK);
+
+ goto done;
+
+bad_arg:
+ return enif_make_badarg(env);
+err:
+ ret = enacl_error_finalized(env);
+done:
+ enif_mutex_unlock(obj->mtx);
+ return ret;
+}
+
+/*
+int
+crypto_secretstream_xchacha20poly1305_push
+ (crypto_secretstream_xchacha20poly1305_state *state,
+ unsigned char *out, unsigned long long *outlen_p,
+ const unsigned char *m, unsigned long long mlen,
+ const unsigned char *ad, unsigned long long adlen, unsigned char tag)
+*/
+ERL_NIF_TERM
+enacl_crypto_secretstream_xchacha20poly1305_push(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]) {
+ ERL_NIF_TERM ret;
+ ErlNifBinary m, ad, out;
+ ErlNifUInt64 tag;
+ enacl_secretstream_ctx *obj = NULL;
+
+ if (argc != 4) {
+ goto bad_arg;
+ }
+
+ if (!enif_get_resource(env, argv[0],
+ (ErlNifResourceType *)enacl_secretstream_ctx_rtype,
+ (void **)&obj)) {
+ goto bad_arg;
+ }
+
+ if (!enif_inspect_binary(env, argv[1], &m)) {
+ goto bad_arg;
+ }
+
+ if (!enif_inspect_binary(env, argv[2], &ad)) {
+ goto bad_arg;
+ }
+
+ if (!enif_get_uint64(env, argv[3], &tag)) {
+ goto bad_arg;
+ }
+
+ if (!enif_alloc_binary(m.size + crypto_secretstream_xchacha20poly1305_ABYTES,
+ &out)) {
+ return enacl_internal_error(env);
+ }
+
+ enif_mutex_lock(obj->mtx);
+ if (!obj->alive) {
+ goto err;
+ }
+
+ crypto_secretstream_xchacha20poly1305_push(obj->state, out.data, NULL, m.data,
+ m.size, ad.data, ad.size, tag);
+
+ if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL) {
+ if (obj->state) {
+ obj->alive = 0;
+ sodium_memzero(obj->state,
+ crypto_secretstream_xchacha20poly1305_statebytes());
+ enif_free(obj->state);
+ obj->state = NULL;
+ }
+ }
+
+ ret = enif_make_binary(env, &out);
+
+ goto done;
+
+bad_arg:
+ return enif_make_badarg(env);
+err:
+ ret = enacl_error_finalized(env);
+ enif_release_binary(&out);
+done:
+ enif_mutex_unlock(obj->mtx);
+ return ret;
+}
+
+/*
+ crypto_secretstream_xchacha20poly1305_pull
+ (crypto_secretstream_xchacha20poly1305_state *state,
+ unsigned char *m, unsigned long long *mlen_p, unsigned char *tag_p,
+ const unsigned char *in, unsigned long long inlen,
+ const unsigned char *ad, unsigned long long adlen)
+ */
+ERL_NIF_TERM
+enacl_crypto_secretstream_xchacha20poly1305_pull(ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]) {
+ ERL_NIF_TERM ret;
+ ErlNifBinary m, in, ad;
+ unsigned char tag;
+ enacl_secretstream_ctx *obj = NULL;
+
+ if (argc != 3) {
+ goto bad_arg;
+ }
+
+ if (!enif_get_resource(env, argv[0],
+ (ErlNifResourceType *)enacl_secretstream_ctx_rtype,
+ (void **)&obj)) {
+ goto bad_arg;
+ }
+
+ if (!enif_inspect_binary(env, argv[1], &in)) {
+ goto bad_arg;
+ }
+
+ if (in.size < crypto_secretstream_xchacha20poly1305_ABYTES) {
+ goto bad_arg;
+ }
+
+ if (!enif_inspect_binary(env, argv[2], &ad)) {
+ goto bad_arg;
+ }
+
+ if (in.size < crypto_secretstream_xchacha20poly1305_ABYTES) {
+ goto bad_arg;
+ }
+
+ if (!enif_alloc_binary(in.size - crypto_secretstream_xchacha20poly1305_ABYTES,
+ &m)) {
+ return enacl_internal_error(env);
+ }
+
+ enif_mutex_lock(obj->mtx);
+ if (!obj->alive) {
+ goto err;
+ }
+
+ if (0 != crypto_secretstream_xchacha20poly1305_pull(obj->state, m.data, NULL,
+ &tag, in.data, in.size,
+ ad.data, ad.size)) {
+ ret = enacl_error_tuple(env, "failed_verification");
+ goto release;
+ }
+
+ if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL) {
+ if (obj->state) {
+ obj->alive = 0;
+ sodium_memzero(obj->state,
+ crypto_secretstream_xchacha20poly1305_statebytes());
+ enif_free(obj->state);
+ obj->state = NULL;
+ }
+ }
+
+ ret = enif_make_tuple2(env, enif_make_binary(env, &m),
+ enif_make_int64(env, tag));
+
+ goto done;
+
+bad_arg:
+ return enif_make_badarg(env);
+err:
+ ret = enacl_error_finalized(env);
+release:
+ enif_release_binary(&m);
+done:
+ enif_mutex_unlock(obj->mtx);
+ return ret;
+}
diff --git a/c_src/secretstream.h b/c_src/secretstream.h
new file mode 100644
index 0000000..4f1f4bd
--- /dev/null
+++ b/c_src/secretstream.h
@@ -0,0 +1,78 @@
+#ifndef ENACL_SECRETSTREAM_H
+#define ENACL_SECRETSTREAM_H
+
+#include
+
+int enacl_init_secretstream_ctx(ErlNifEnv *env);
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_ABYTES(
+ ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]
+ );
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_HEADERBYTES(
+ ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]
+ );
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_KEYBYTES(
+ ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]
+ );
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX(
+ ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]
+ );
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_MESSAGE(
+ ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]
+ );
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_PUSH(
+ ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]
+ );
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_REKEY(
+ ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]
+ );
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_FINAL(
+ ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]
+ );
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_keygen(
+ ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]
+ );
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_init_push(
+ ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]
+ );
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_init_pull(
+ ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]
+ );
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_rekey(
+ ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]
+ );
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_push(
+ ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]
+ );
+
+ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_pull(
+ ErlNifEnv *env, int argc,
+ const ERL_NIF_TERM argv[]
+ );
+
+#endif
diff --git a/c_src/sign.c b/c_src/sign.c
new file mode 100644
index 0000000..fb06203
--- /dev/null
+++ b/c_src/sign.c
@@ -0,0 +1,484 @@
+#include
+
+#include
+
+#include "enacl.h"
+#include "sign.h"
+
+typedef struct enacl_sign_ctx {
+ ErlNifMutex *mtx;
+ crypto_sign_state *state; // The underlying signature state
+ int alive; // Is the context still valid for updates/finalization
+} enacl_sign_ctx;
+
+ErlNifResourceType *enacl_sign_ctx_rtype = NULL;
+
+void enacl_sign_ctx_dtor(ErlNifEnv *env, enacl_sign_ctx *);
+
+int enacl_init_sign_ctx(ErlNifEnv *env) {
+ enacl_sign_ctx_rtype =
+ enif_open_resource_type(env, NULL, "enacl_sign_context",
+ (ErlNifResourceDtor *)enacl_sign_ctx_dtor,
+ ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER, NULL);
+
+ if (enacl_sign_ctx_rtype == NULL)
+ return 0;
+
+ return 1;
+}
+
+void enacl_sign_ctx_dtor(ErlNifEnv *env, enacl_sign_ctx *obj) {
+ if (!obj->alive)
+ return;
+
+ if (obj->state) {
+ sodium_memzero(obj->state, crypto_sign_statebytes());
+ enif_free(obj->state);
+ }
+
+ if (obj->mtx != NULL)
+ enif_mutex_destroy(obj->mtx);
+
+ return;
+}
+
+/*
+ int crypto_sign_init(crypto_sign_state *state)
+ */
+
+ERL_NIF_TERM enacl_crypto_sign_init(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ERL_NIF_TERM ret;
+ enacl_sign_ctx *obj = NULL;
+
+ if (argc != 0)
+ goto bad_arg;
+
+ if ((obj = enif_alloc_resource(enacl_sign_ctx_rtype,
+ sizeof(enacl_sign_ctx))) == NULL) {
+ ret = enacl_internal_error(env);
+ goto done;
+ }
+ obj->alive = 0;
+ obj->state = enif_alloc(crypto_sign_statebytes());
+ if (obj->state == NULL) {
+ goto release;
+ }
+ obj->alive = 1;
+
+ if ((obj->mtx = enif_mutex_create("enacl.sign")) == NULL) {
+ goto free;
+ }
+
+ if (0 != crypto_sign_init(obj->state)) {
+ goto free;
+ }
+
+ // Create return values
+ ret = enif_make_resource(env, obj);
+
+ goto release;
+
+bad_arg:
+ return enif_make_badarg(env);
+free:
+ if (obj->alive)
+ if (obj->state != NULL) {
+ sodium_memzero(obj->state, crypto_sign_statebytes());
+ enif_free(obj->state);
+ obj->state = NULL;
+ }
+release:
+ // This also frees the mutex via the destructor
+ enif_release_resource(obj);
+done:
+ return ret;
+}
+
+/*
+ int crypto_sign_update(crypto_sign_state *state,
+ const unsigned char *m,
+ unsigned long long mlen);
+ */
+
+ERL_NIF_TERM enacl_crypto_sign_update(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ERL_NIF_TERM ret;
+ enacl_sign_ctx *obj = NULL;
+ ErlNifBinary data;
+
+ // Validate the arguments
+ if (argc != 2)
+ goto bad_arg;
+ if (!enif_get_resource(env, argv[0], enacl_sign_ctx_rtype, (void **)&obj))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[1], &data))
+ goto bad_arg;
+
+ enif_mutex_lock(obj->mtx);
+ if (!obj->alive) {
+ ret = enacl_error_finalized(env);
+ goto done;
+ }
+
+ if (0 != crypto_sign_update(obj->state, data.data, data.size)) {
+ ret = enacl_internal_error(env); // This should never be hit
+ goto done;
+ }
+
+ ret = argv[0];
+ goto done;
+
+bad_arg:
+ return enif_make_badarg(env);
+done:
+ enif_mutex_unlock(obj->mtx);
+ return ret;
+}
+
+ERL_NIF_TERM enacl_crypto_sign_final_create(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ERL_NIF_TERM ret;
+ enacl_sign_ctx *obj = NULL;
+ ErlNifBinary sk, sig;
+ unsigned long long siglen;
+
+ if (argc != 2)
+ goto bad_arg;
+ if (!enif_get_resource(env, argv[0], enacl_sign_ctx_rtype, (void **)&obj))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[1], &sk))
+ goto bad_arg;
+ if (sk.size != crypto_sign_SECRETKEYBYTES)
+ goto bad_arg;
+
+ enif_mutex_lock(obj->mtx);
+ if (!obj->alive) {
+ ret = enacl_error_finalized(env);
+ goto done;
+ }
+
+ if (!enif_alloc_binary(crypto_sign_BYTES, &sig)) {
+ ret = enacl_internal_error(env);
+ goto done;
+ }
+
+ crypto_sign_final_create(obj->state, sig.data, &siglen, sk.data);
+
+ ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK);
+ ERL_NIF_TERM signature = enif_make_binary(env, &sig);
+
+ ret = enif_make_tuple2(env, ok, signature);
+ goto cleanup;
+
+bad_arg:
+ return enif_make_badarg(env);
+cleanup:
+ obj->alive = 0;
+ sodium_memzero(obj->state, crypto_sign_statebytes());
+ enif_free(obj->state);
+ obj->state = NULL;
+done:
+ enif_mutex_unlock(obj->mtx);
+ return ret;
+}
+
+ERL_NIF_TERM enacl_crypto_sign_final_verify(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary pk, sig;
+ enacl_sign_ctx *obj = NULL;
+ ERL_NIF_TERM ret;
+
+ if (argc != 3)
+ goto bad_arg;
+ if (!enif_get_resource(env, argv[0], enacl_sign_ctx_rtype, (void **)&obj))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[1], &sig))
+ goto bad_arg;
+ if (!enif_inspect_binary(env, argv[2], &pk))
+ goto bad_arg;
+ if (pk.size != crypto_sign_PUBLICKEYBYTES)
+ goto bad_arg;
+
+ enif_mutex_lock(obj->mtx);
+ if (!obj->alive) {
+ ret = enacl_error_finalized(env);
+ goto done;
+ }
+
+ if (0 == crypto_sign_final_verify(obj->state, sig.data, pk.data)) {
+ ret = enif_make_atom(env, "true");
+ } else {
+ ret = enif_make_atom(env, "false");
+ }
+ // Mark as done
+ goto cleanup;
+
+bad_arg:
+ return enif_make_badarg(env);
+cleanup:
+ // Get rid of the context and mark it as dead
+ obj->alive = 0;
+ sodium_memzero(obj->state, crypto_sign_statebytes());
+ enif_free(obj->state);
+ obj->state = NULL;
+done:
+ enif_mutex_unlock(obj->mtx);
+ return ret;
+}
+
+/* Ed 25519 */
+ERL_NIF_TERM
+enacl_crypto_sign_ed25519_keypair(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary pk, sk;
+
+ if (argc != 0) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(crypto_sign_ed25519_PUBLICKEYBYTES, &pk)) {
+ return enacl_internal_error(env);
+ }
+
+ if (!enif_alloc_binary(crypto_sign_ed25519_SECRETKEYBYTES, &sk)) {
+ enif_release_binary(&pk);
+ return enacl_internal_error(env);
+ }
+
+ crypto_sign_ed25519_keypair(pk.data, sk.data);
+
+ return enif_make_tuple2(env, enif_make_binary(env, &pk),
+ enif_make_binary(env, &sk));
+}
+
+ERL_NIF_TERM
+enacl_crypto_sign_ed25519_sk_to_pk(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary pk, sk;
+
+ if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &sk)) ||
+ (sk.size != crypto_sign_ed25519_SECRETKEYBYTES)) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(crypto_sign_ed25519_PUBLICKEYBYTES, &pk)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_sign_ed25519_sk_to_pk(pk.data, sk.data);
+
+ return enif_make_binary(env, &pk);
+}
+
+ERL_NIF_TERM
+enacl_crypto_sign_ed25519_public_to_curve25519(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary curve25519_pk, ed25519_pk;
+
+ if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &ed25519_pk)) ||
+ (ed25519_pk.size != crypto_sign_ed25519_PUBLICKEYBYTES)) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &curve25519_pk)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_sign_ed25519_pk_to_curve25519(curve25519_pk.data, ed25519_pk.data);
+
+ return enif_make_binary(env, &curve25519_pk);
+}
+
+ERL_NIF_TERM
+enacl_crypto_sign_ed25519_secret_to_curve25519(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary curve25519_sk, ed25519_sk;
+
+ if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &ed25519_sk)) ||
+ (ed25519_sk.size != crypto_sign_ed25519_SECRETKEYBYTES)) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &curve25519_sk)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_sign_ed25519_sk_to_curve25519(curve25519_sk.data, ed25519_sk.data);
+
+ return enif_make_binary(env, &curve25519_sk);
+}
+
+ERL_NIF_TERM
+enacl_crypto_sign_ed25519_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_sign_ed25519_PUBLICKEYBYTES);
+}
+
+ERL_NIF_TERM
+enacl_crypto_sign_ed25519_SECRETKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_sign_ed25519_SECRETKEYBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_sign_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_sign_PUBLICKEYBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_sign_SECRETKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_sign_SECRETKEYBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_sign_SEEDBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ return enif_make_int64(env, crypto_sign_SEEDBYTES);
+}
+
+ERL_NIF_TERM enacl_crypto_sign_keypair(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary pk, sk;
+
+ if (argc != 0) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(crypto_sign_PUBLICKEYBYTES, &pk)) {
+ return enacl_internal_error(env);
+ }
+
+ if (!enif_alloc_binary(crypto_sign_SECRETKEYBYTES, &sk)) {
+ enif_release_binary(&pk);
+ return enacl_internal_error(env);
+ }
+
+ crypto_sign_keypair(pk.data, sk.data);
+
+ return enif_make_tuple2(env, enif_make_binary(env, &pk),
+ enif_make_binary(env, &sk));
+}
+
+ERL_NIF_TERM enacl_crypto_sign_seed_keypair(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary pk, sk, seed;
+
+ if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &seed))) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(crypto_sign_PUBLICKEYBYTES, &pk)) {
+ return enacl_internal_error(env);
+ }
+
+ if (!enif_alloc_binary(crypto_sign_SECRETKEYBYTES, &sk)) {
+ enif_release_binary(&pk);
+ return enacl_internal_error(env);
+ }
+
+ crypto_sign_seed_keypair(pk.data, sk.data, seed.data);
+
+ return enif_make_tuple2(env, enif_make_binary(env, &pk),
+ enif_make_binary(env, &sk));
+}
+
+ERL_NIF_TERM enacl_crypto_sign(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary m, sk, sm;
+ unsigned long long smlen;
+
+ if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
+ (!enif_inspect_binary(env, argv[1], &sk))) {
+ return enif_make_badarg(env);
+ }
+
+ if (sk.size != crypto_sign_SECRETKEYBYTES) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(m.size + crypto_sign_BYTES, &sm)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_sign(sm.data, &smlen, m.data, m.size, sk.data);
+
+ return enif_make_sub_binary(env, enif_make_binary(env, &sm), 0, smlen);
+}
+
+ERL_NIF_TERM enacl_crypto_sign_open(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary m, sm, pk;
+ unsigned long long mlen;
+
+ if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &sm)) ||
+ (!enif_inspect_binary(env, argv[1], &pk))) {
+ return enif_make_badarg(env);
+ }
+
+ if (pk.size != crypto_sign_PUBLICKEYBYTES) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(sm.size, &m)) {
+ return enacl_internal_error(env);
+ }
+
+ if (0 == crypto_sign_open(m.data, &mlen, sm.data, sm.size, pk.data)) {
+ ERL_NIF_TERM ret_ok = enif_make_atom(env, ATOM_OK);
+ ERL_NIF_TERM ret_bin =
+ enif_make_sub_binary(env, enif_make_binary(env, &m), 0, mlen);
+ return enif_make_tuple2(env, ret_ok, ret_bin);
+ } else {
+ enif_release_binary(&m);
+ return enacl_error_tuple(env, "failed_verification");
+ }
+}
+
+ERL_NIF_TERM enacl_crypto_sign_detached(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary m, sk, sig;
+ unsigned long long siglen;
+
+ if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
+ (!enif_inspect_binary(env, argv[1], &sk))) {
+ return enif_make_badarg(env);
+ }
+
+ if (sk.size != crypto_sign_SECRETKEYBYTES) {
+ return enif_make_badarg(env);
+ }
+
+ if (!enif_alloc_binary(crypto_sign_BYTES, &sig)) {
+ return enacl_internal_error(env);
+ }
+
+ crypto_sign_detached(sig.data, &siglen, m.data, m.size, sk.data);
+
+ return enif_make_binary(env, &sig);
+}
+
+ERL_NIF_TERM
+enacl_crypto_sign_verify_detached(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]) {
+ ErlNifBinary m, sig, pk;
+
+ if ((argc != 3) || (!enif_inspect_binary(env, argv[0], &sig)) ||
+ (!enif_inspect_iolist_as_binary(env, argv[1], &m)) ||
+ (!enif_inspect_binary(env, argv[2], &pk))) {
+ return enif_make_badarg(env);
+ }
+
+ if (sig.size != crypto_sign_BYTES) {
+ return enif_make_badarg(env);
+ }
+
+ if (pk.size != crypto_sign_PUBLICKEYBYTES) {
+ return enif_make_badarg(env);
+ }
+
+ if (0 == crypto_sign_verify_detached(sig.data, m.data, m.size, pk.data)) {
+ return enif_make_atom(env, ATOM_TRUE);
+ } else {
+ return enif_make_atom(env, ATOM_FALSE);
+ }
+}
diff --git a/c_src/sign.h b/c_src/sign.h
new file mode 100644
index 0000000..48f8e01
--- /dev/null
+++ b/c_src/sign.h
@@ -0,0 +1,70 @@
+#ifndef ENACL_SIGN_H
+#define ENACL_SIGN_H
+
+#include
+
+int enacl_init_sign_ctx(ErlNifEnv *env);
+
+ERL_NIF_TERM enacl_crypto_sign_init(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_sign_update(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_sign_final_create(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_sign_final_verify(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_sign_ed25519_keypair(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_sign_ed25519_sk_to_pk(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM
+enacl_crypto_sign_ed25519_public_to_curve25519(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM
+enacl_crypto_sign_ed25519_secret_to_curve25519(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM
+enacl_crypto_sign_ed25519_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM
+enacl_crypto_sign_ed25519_SECRETKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_sign_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_sign_SECRETKEYBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_sign_SEEDBYTES(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_sign_keypair(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_sign_seed_keypair(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_sign(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_sign_open(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM enacl_crypto_sign_detached(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+ERL_NIF_TERM
+enacl_crypto_sign_verify_detached(ErlNifEnv *env, int argc,
+ ERL_NIF_TERM const argv[]);
+
+#endif
\ No newline at end of file
diff --git a/eqc_test/enacl_eqc.erl b/eqc_test/enacl_eqc.erl
index e2978dc..6093a5b 100644
--- a/eqc_test/enacl_eqc.erl
+++ b/eqc_test/enacl_eqc.erl
@@ -1,6 +1,6 @@
-module(enacl_eqc).
-include_lib("eqc/include/eqc.hrl").
--compile(export_all).
+-compile([export_all, nowarn_export_all]).
-ifndef(mini).
-compile({parse_transform, eqc_parallelize}).
@@ -83,8 +83,8 @@ v_binary(_, _) -> false.
%% Typical generators based on the binaries
-nonce() -> g_binary(enacl:box_nonce_size()).
-nonce_valid(N) -> v_binary(enacl:box_nonce_size(), N).
+nonce() -> g_binary(enacl:box_NONCEBYTES()).
+nonce_valid(N) -> v_binary(enacl:box_NONCEBYTES(), N).
%% Generator of natural numbers
g_nat() ->
@@ -111,10 +111,10 @@ keypair_bad() ->
#{ public := PK, secret := SK} = enacl:box_keypair(),
case X of
pk ->
- PKBytes = enacl:box_public_key_bytes(),
+ PKBytes = enacl:box_PUBLICKEYBYTES(),
{oneof([return(a), nat(), ?SUCHTHAT(B, binary(), byte_size(B) /= PKBytes)]), SK};
sk ->
- SKBytes = enacl:box_secret_key_bytes(),
+ SKBytes = enacl:box_SECRETKEYBYTES(),
{PK, oneof([return(a), nat(), ?SUCHTHAT(B, binary(), byte_size(B) /= SKBytes)])}
end
end).
@@ -122,6 +122,35 @@ keypair_bad() ->
keypair() ->
?FAULT(keypair_bad(), keypair_good()).
+kx_keypair_good() ->
+ #{ public := PK, secret := SK} = enacl:kx_keypair(),
+ {PK, SK}.
+
+kx_keypair_bad() ->
+ ?LET(X, elements([pk, sk]),
+ begin
+ #{ public := PK, secret := SK} = enacl:box_keypair(),
+ case X of
+ pk ->
+ PKBytes = enacl:kx_public_key_size(),
+ {oneof([return(a), nat(), ?SUCHTHAT(B, binary(), byte_size(B) /= PKBytes)]), SK};
+ sk ->
+ SKBytes = enacl:kx_secret_key_size(),
+ {PK, oneof([return(a), nat(), ?SUCHTHAT(B, binary(), byte_size(B) /= SKBytes)])}
+ end
+ end).
+
+g_generichash_data() ->
+ binary().
+
+g_generichash_key() ->
+ ?LET({Min, Max}, {return(enacl_nif:crypto_generichash_KEYBYTES_MIN()), return(enacl_nif:crypto_generichash_KEYBYTES_MAX())},
+ largebinary({limit, Min, Max})).
+
+g_generichash_size() ->
+ ?LET({Min, Max}, {return(enacl_nif:crypto_generichash_BYTES_MIN()), return(enacl_nif:crypto_generichash_BYTES_MAX())},
+ choose(Min, Max)).
+
%% CRYPTO BOX
%% ---------------------------
%% * box/4
@@ -130,8 +159,8 @@ keypair() ->
%% * box_afternm/3
%% * box_open_afternm/3
keypair_valid(PK, SK) when is_binary(PK), is_binary(SK) ->
- PKBytes = enacl:box_public_key_bytes(),
- SKBytes = enacl:box_secret_key_bytes(),
+ PKBytes = enacl:box_PUBLICKEYBYTES(),
+ SKBytes = enacl:box_SECRETKEYBYTES(),
byte_size(PK) == PKBytes andalso byte_size(SK) == SKBytes;
keypair_valid(_PK, _SK) -> false.
@@ -235,11 +264,11 @@ beforenm_key() ->
oneof([
elements([a,b,c]),
real(),
- ?SUCHTHAT(X, binary(), byte_size(X) /= enacl:box_beforenm_bytes())
+ ?SUCHTHAT(X, binary(), byte_size(X) /= enacl:box_BEFORENMBYTES())
])
end).
-v_key(K) when is_binary(K) -> byte_size(K) == enacl:box_beforenm_bytes();
+v_key(K) when is_binary(K) -> byte_size(K) == enacl:box_BEFORENMBYTES();
v_key(_) -> false.
prop_beforenm_correct() ->
@@ -295,11 +324,11 @@ sign_keypair_bad() ->
KP = enacl:sign_keypair(),
case X of
pk ->
- Sz = enacl:sign_keypair_public_size(),
+ Sz = enacl:sign_PUBLICBYTES(),
?LET(Wrong, oneof([a, int(), ?SUCHTHAT(B, binary(), byte_size(B) /= Sz)]),
KP#{ public := Wrong });
sk ->
- Sz = enacl:sign_keypair_secret_size(),
+ Sz = enacl:sign_SECRETBYTES(),
?LET(Wrong, oneof([a, int(), ?SUCHTHAT(B, binary(), byte_size(B) /= Sz)]),
KP#{ secret := Wrong })
end
@@ -313,12 +342,12 @@ sign_keypair() ->
sign_keypair_public_valid(#{ public := Public })
when is_binary(Public) ->
- byte_size(Public) == enacl:sign_keypair_public_size();
+ byte_size(Public) == enacl:sign_PUBLICBYTES();
sign_keypair_public_valid(_) -> false.
sign_keypair_secret_valid(#{ secret := Secret })
when is_binary(Secret) ->
- byte_size(Secret) == enacl:sign_keypair_secret_size();
+ byte_size(Secret) == enacl:sign_SECRETBYTES();
sign_keypair_secret_valid(_) -> false.
sign_keypair_valid(KP) ->
@@ -379,18 +408,18 @@ signed_message_good_d(M) ->
end)}]).
signed_message_bad() ->
- Sz = enacl:sign_keypair_public_size(),
+ Sz = enacl:sign_PUBLICBYTES(),
{binary(), oneof([a, int(), ?SUCHTHAT(B, binary(Sz), byte_size(B) /= Sz)])}.
signed_message_bad_d() ->
- Sz = enacl:sign_keypair_public_size(),
+ Sz = enacl:sign_PUBLICBYTES(),
{binary(), oneof([a, int(), ?SUCHTHAT(B, binary(Sz), byte_size(B) /= Sz)])}.
signed_message(M) ->
?FAULT(signed_message_bad(), signed_message_good(M)).
signed_message_d(M) ->
- ?FAULT(signed_message_bad(), signed_message_good(M)).
+ ?FAULT(signed_message_bad_d(), signed_message_good_d(M)).
signed_message_valid({valid, _}, _) -> true;
signed_message_valid({invalid, _}, _) -> true;
@@ -403,9 +432,9 @@ prop_sign_detached_open() ->
true ->
case SignMsg of
{valid, Sig} ->
- equals({ok, Msg}, enacl:sign_verify_detached(Sig, Msg, PK));
+ equals(true, enacl:sign_verify_detached(Sig, Msg, PK));
{invalid, Sig} ->
- equals({error, failed_verification}, enacl:sign_verify_detached(Sig, Msg, PK))
+ equals(false, enacl:sign_verify_detached(Sig, Msg, PK))
end;
false ->
badargs(fun() -> enacl:sign_verify_detached(SignMsg, Msg, PK) end)
@@ -467,19 +496,19 @@ prop_seal_box_correct() ->
%% * secretbox/3
%% * secretbo_open/3
secret_key_good() ->
- Sz = enacl:secretbox_key_size(),
+ Sz = enacl:secretbox_KEYBYTES(),
binary(Sz).
secret_key_bad() ->
oneof([return(a),
nat(),
- ?SUCHTHAT(B, binary(), byte_size(B) /= enacl:secretbox_key_size())]).
+ ?SUCHTHAT(B, binary(), byte_size(B) /= enacl:secretbox_KEYBYTES())]).
secret_key() ->
?FAULT(secret_key_bad(), secret_key_good()).
secret_key_valid(SK) when is_binary(SK) ->
- Sz = enacl:secretbox_key_size(),
+ Sz = enacl:secretbox_KEYBYTES(),
byte_size(SK) == Sz;
secret_key_valid(_SK) -> false.
@@ -525,20 +554,45 @@ prop_secretbox_failure_integrity() ->
%% ------------------------------------------------------------
%% * aead_chacha20poly1305_encrypt/4,
%% * aead_chacha20poly1305_decrypt/4,
-prop_aead_chacha20poly1305() ->
+prop_aead_chacha20poly1305_ietf() ->
+ NPubBytes = enacl:aead_chacha20poly1305_ietf_NPUBBYTES(),
?FORALL({Key, Msg, AD, Nonce},
- {binary(32), binary(), ?LET(ADBytes, choose(0,16), binary(ADBytes)), largeint()},
+ {binary(32), binary(), ?LET(ADBytes, choose(0,16), binary(ADBytes)), binary(NPubBytes)},
begin
- EncryptMsg = enacl:aead_chacha20poly1305_encrypt(Key, Nonce, AD, Msg),
- equals(enacl:aead_chacha20poly1305_decrypt(Key, Nonce, AD, EncryptMsg), Msg)
+ EncryptMsg = enacl:aead_chacha20poly1305_ietf_encrypt(Msg, AD, Nonce, Key),
+ equals(enacl:aead_chacha20poly1305_ietf_decrypt(EncryptMsg, AD, Nonce, Key), Msg)
end).
-prop_aead_chacha20poly1305_fail() ->
+prop_aead_chacha20poly1305_ietf_fail() ->
+ NPubBytes = enacl:aead_chacha20poly1305_ietf_NPUBBYTES(),
?FORALL({Key, Msg, AD, Nonce},
- {binary(32), binary(), ?LET(ADBytes, choose(0,16), binary(ADBytes)), largeint()},
+ {binary(32), binary(), ?LET(ADBytes, choose(0,16), binary(ADBytes)), binary(NPubBytes)},
begin
- EncryptMsg = enacl:aead_chacha20poly1305_encrypt(Key, Nonce, AD, Msg),
- case enacl:aead_chacha20poly1305_decrypt(Key, Nonce, AD, <<0:8, EncryptMsg/binary>>) of
+ EncryptMsg = enacl:aead_chacha20poly1305_ietf_encrypt(Msg, AD, Nonce, Key),
+ case enacl:aead_chacha20poly1305_ietf_decrypt(<<0:8, EncryptMsg/binary>>, AD, Nonce, Key) of
+ {error, _} -> true;
+ _ -> false
+ end
+ end).
+
+%% * aead_xchacha20poly1305_encrypt/4,
+%% * aead_xchacha20poly1305_decrypt/4,
+prop_aead_xchacha20poly1305_ietf() ->
+ NPubBytes = enacl:aead_xchacha20poly1305_ietf_NPUBBYTES(),
+ ?FORALL({Key, Msg, AD, Nonce},
+ {binary(32), binary(), ?LET(ADBytes, choose(0,16), binary(ADBytes)), binary(NPubBytes)},
+ begin
+ EncryptMsg = enacl:aead_xchacha20poly1305_ietf_encrypt(Msg, AD, Nonce, Key),
+ equals(enacl:aead_xchacha20poly1305_ietf_decrypt(EncryptMsg, AD, Nonce, Key), Msg)
+ end).
+
+prop_aead_xchacha20poly1305_ietf_fail() ->
+ NPubBytes = enacl:aead_xchacha20poly1305_ietf_NPUBBYTES(),
+ ?FORALL({Key, Msg, AD, Nonce},
+ {binary(32), binary(), ?LET(ADBytes, choose(0,16), binary(ADBytes)), binary(NPubBytes)},
+ begin
+ EncryptMsg = enacl:aead_xchacha20poly1305_ietf_encrypt(Msg, AD, Nonce, Key),
+ case enacl:aead_xchacha20poly1305_ietf_decrypt(<<0:8, EncryptMsg/binary>>, AD, Nonce, Key) of
{error, _} -> true;
_ -> false
end
@@ -564,23 +618,26 @@ xor_bytes(<>, <>) ->
[A bxor B | xor_bytes(As, Bs)];
xor_bytes(<<>>, <<>>) -> [].
-%% prop_stream_xor_correct() ->
-%% ?FORALL({Msg, Nonce, Key},
-%% {?FAULT_RATE(1, 40, g_iodata()),
-%% ?FAULT_RATE(1, 40, nonce()),
-%% ?FAULT_RATE(1, 40, secret_key())},
-%% case v_iodata(Msg) andalso nonce_valid(Nonce) andalso secret_key_valid(Key) of
-%% true ->
-%% Stream = enacl:stream(iolist_size(Msg), Nonce, Key),
-%% CipherText = enacl:stream_xor(Msg, Nonce, Key),
-%% StreamXor = enacl:stream_xor(CipherText, Nonce, Key),
-%% conjunction([
-%% {'xor', equals(iolist_to_binary(Msg), StreamXor)},
-%% {stream, equals(iolist_to_binary(xor_bytes(Stream, iolist_to_binary(Msg))), CipherText)}
-%% ]);
-%% false ->
-%% badargs(fun() -> enacl:stream_xor(Msg, Nonce, Key) end)
-%% end).
+positive() ->
+ ?LET(N, nat(), N+1).
+
+chacha20_nonce() ->
+ Sz = enacl:stream_chacha20_NONCEBYTES(),
+ binary(Sz).
+
+chacha20_key() ->
+ Sz = enacl:stream_chacha20_KEYBYTES(),
+ binary(Sz).
+
+prop_stream_chacha20_correct() ->
+ ?FORALL(Len, positive(),
+ ?FORALL({Msg, Nonce, Key}, {binary(Len), chacha20_nonce(), chacha20_key()},
+ begin
+ CT = enacl:stream_chacha20_xor(Msg, Nonce, Key),
+ Stream = enacl:stream_chacha20(Len, Nonce, Key),
+ CT2 = list_to_binary(xor_bytes(Stream, Msg)),
+ equals(CT, CT2)
+ end)).
%% CRYPTO AUTH
%% ------------------------------------------------------------
@@ -599,19 +656,19 @@ prop_auth_correct() ->
end).
authenticator_bad() ->
- oneof([a, int(), ?SUCHTHAT(X, binary(), byte_size(X) /= enacl:auth_size())]).
+ oneof([a, int(), ?SUCHTHAT(X, binary(), byte_size(X) /= enacl:auth_BYTES())]).
authenticator_good(Msg, Key) when is_binary(Key) ->
- Sz = enacl:secretbox_key_size(),
+ Sz = enacl:secretbox_KEYBYTES(),
case v_iodata(Msg) andalso byte_size(Key) == Sz of
true ->
- frequency([{1, ?LAZY({invalid, binary(enacl:auth_size())})},
+ frequency([{1, ?LAZY({invalid, binary(enacl:auth_BYTES())})},
{3, return({valid, enacl:auth(Msg, Key)})}]);
false ->
- binary(enacl:auth_size())
+ binary(enacl:auth_BYTES())
end;
authenticator_good(_Msg, _Key) ->
- binary(enacl:auth_size()).
+ binary(enacl:auth_BYTES()).
authenticator(Msg, Key) ->
?FAULT(authenticator_bad(), authenticator_good(Msg, Key)).
@@ -654,19 +711,19 @@ prop_onetimeauth_correct() ->
end).
ot_authenticator_bad() ->
- oneof([a, int(), ?SUCHTHAT(X, binary(), byte_size(X) /= enacl:onetime_auth_size())]).
+ oneof([a, int(), ?SUCHTHAT(X, binary(), byte_size(X) /= enacl:onetime_auth_BYTES())]).
ot_authenticator_good(Msg, Key) when is_binary(Key) ->
- Sz = enacl:secretbox_key_size(),
+ Sz = enacl:secretbox_KEYBYTES(),
case v_iodata(Msg) andalso byte_size(Key) == Sz of
true ->
- frequency([{1, ?LAZY({invalid, binary(enacl:onetime_auth_size())})},
+ frequency([{1, ?LAZY({invalid, binary(enacl:onetime_auth_BYTES())})},
{3, return({valid, enacl:onetime_auth(Msg, Key)})}]);
false ->
- binary(enacl:onetime_auth_size())
+ binary(enacl:onetime_auth_BYTES())
end;
ot_authenticator_good(_Msg, _Key) ->
- binary(enacl:auth_size()).
+ binary(enacl:auth_BYTES()).
ot_authenticator(Msg, Key) ->
?FAULT(ot_authenticator_bad(), ot_authenticator_good(Msg, Key)).
@@ -704,6 +761,13 @@ pwhash(Passwd, Salt) ->
error:badarg -> badarg
end.
+pwhash(Password, Salt, Ops, Mem, Alg) ->
+ try
+ enacl:pwhsah(Password, Salt, Ops, Mem, Alg)
+ catch
+ error:badarg -> badarg
+ end.
+
pwhash_str(Passwd) ->
try
enacl:pwhash_str(Passwd)
@@ -718,13 +782,29 @@ pwhash_str_verify(PasswdHash, Passwd) ->
error:badarg -> badarg
end.
+prop_pwhash() ->
+ ?FORALL({Password, Salt, OLimit, MLimit, Alg},
+ {binary(16),
+ binary(16),
+ elements([interactive, moderate]), %% These could add senstitive, but are too runtime-expensive
+ elements([interactive, moderate]), %% And that is for a reason.
+ elements([default, 'argon2id13'])}, %% Argon2I13 uses different limits, so it is kept out as
+ %% this would otherwise fail
+ begin
+ Bin1 = enacl:pwhash(Password, Salt, OLimit, MLimit, Alg),
+ Bin2 = enacl:pwhash(Password, Salt, OLimit, MLimit, Alg),
+ equals(Bin1, Bin2)
+ end).
+
prop_pwhash_str_verify() ->
- ?FORALL({Passwd},
- {?FAULT_RATE(1, 40, g_iodata())},
+ ?FORALL({Passwd, OLimit, MLimit},
+ {?FAULT_RATE(1, 40, g_iodata()),
+ elements([interactive, moderate]),
+ elements([interactive, moderate])},
begin
case v_iodata(Passwd) of
true ->
- {ok, Ascii} = enacl:pwhash_str(Passwd),
+ Ascii = enacl:pwhash_str(Passwd, OLimit, MLimit),
S = enacl:pwhash_str_verify(Ascii, Passwd),
equals(S, true);
false ->
@@ -758,6 +838,41 @@ prop_crypto_hash_neq() ->
enacl:hash(X) /= enacl:hash(Y)
).
+prop_crypto_shorthash_eq() ->
+ ?FORALL(X, g_iodata(),
+ case v_iodata(X) of
+ true -> equals(enacl:hash(X), enacl:hash(X));
+ false ->
+ try
+ enacl:hash(X),
+ false
+ catch
+ error:badarg -> true
+ end
+ end
+ ).
+prop_crypto_generichash_eq() ->
+ ?FORALL({Sz, X, Key}, {g_generichash_size(), g_generichash_data(), g_generichash_key()},
+ equals(enacl:generichash(Sz, X, Key), enacl:generichash(Sz, X, Key))).
+
+generichash_loop(S, []) -> S;
+generichash_loop(S, [M|Ms]) ->
+ S2 = enacl:generichash_update(S, M),
+ generichash_loop(S2, Ms).
+
+prop_crypto_generichash_multi_part_eq() ->
+ ?FORALL({Sz, Xs, Key}, {g_generichash_size(), list(g_generichash_data()), g_generichash_key()},
+ begin
+ S1 = generichash_loop(enacl:generichash_init(Sz, Key), Xs),
+ S2 = generichash_loop(enacl:generichash_init(Sz, Key), Xs),
+ equals(enacl:generichash_final(S1), enacl:generichash_final(S2))
+ end).
+
+prop_crypto_shorthash_neq() ->
+ ?FORALL({X, Y}, diff_pair(),
+ enacl:hash(X) /= enacl:hash(Y)
+ ).
+
%% STRING COMPARISON
%% -------------------------
%% * verify_16/2,
@@ -817,7 +932,8 @@ prop_randombytes() ->
?FORALL(X, g_nat(),
case is_nat(X) of
true ->
- is_binary(enacl:randombytes(X));
+ R = enacl:randombytes(X),
+ is_binary(R) andalso (byte_size(R) == X);
false ->
try
enacl:randombytes(X),
@@ -828,8 +944,84 @@ prop_randombytes() ->
end
end).
+prop_randombytes_uint32() ->
+ ?FORALL(_, return(x),
+ begin
+ V = enacl:randombytes_uint32(),
+ is_integer(V)
+ end).
+
+%% KX
+%% ---------------------------
+prop_kx() ->
+ ?FORALL({{CPK, CSK}, {SPK, SSK}}, {kx_keypair_good(), kx_keypair_good()},
+ begin
+ #{ client_tx := CTX, client_rx := CRX} = enacl:kx_client_session_keys(CPK, CSK, SPK),
+ #{ server_tx := STX, server_rx := SRX} = enacl:kx_server_session_keys(SPK, SSK, CPK),
+ %% This keypair must be shared in both directions
+ conjunction([{ctx_srx, equals(CTX, SRX)}, {stx_crx, equals(STX, CRX)}])
+ end).
+
+%% SCRAMBLING
+prop_scramble_block() ->
+ ?FORALL({Block, Key}, {binary(16), eqc_gen:largebinary(32)},
+ is_binary(enacl_ext:scramble_block_16(Block, Key))).
+
+%% Scala multiplication
+prop_scalarmult() ->
+ Bytes = 32,
+ ?FORALL({S1, S2, Basepoint}, {binary(Bytes), binary(Bytes), binary(Bytes)},
+ equals(enacl:curve25519_scalarmult(S1,
+ enacl:curve25519_scalarmult(S2, Basepoint)),
+ enacl:curve25519_scalarmult(S2,
+ enacl:curve25519_scalarmult(S1, Basepoint)))
+ ).
+
+%% Secretstream
+secretstream_key() ->
+ ?LET(K, enacl:secretstream_xchacha20poly1305_keygen(), K).
+
+secretstream_msg() ->
+ ?LET({Tag, AD, Msg}, {oneof([message,rekey,push]), binary(), binary()},
+ {Tag, AD, Msg}).
+
+secretstream_msgs() ->
+ ?LET({Ms, {_, AD, Msg}}, {list(secretstream_msg()), secretstream_msg()},
+ Ms ++ [{final, AD, Msg}]).
+
+push_messages(_State, []) ->
+ [];
+push_messages(State, [{Tag, AD, Msg}|Next]) ->
+ Block = enacl:secretstream_xchacha20poly1305_push(State, Msg, AD, Tag),
+ [Block|push_messages(State, Next)].
+
+pull_messages(_State, [], []) ->
+ true;
+pull_messages(State, [B|Bs], [{_Tag, AD, _Msg}=Expect|Next]) ->
+ {Msgx, Tagx} = enacl:secretstream_xchacha20poly1305_pull(State, B, AD),
+ case equals(Expect, {Tagx, AD, Msgx}) of
+ true ->
+ pull_messages(State, Bs, Next);
+ R ->
+ R
+ end.
+
+prop_secretstream() ->
+ ?FORALL({Key, Msgs}, {secretstream_key(), secretstream_msgs()},
+ begin
+ %% Encrypt
+ {Header, State} = enacl:secretstream_xchacha20poly1305_init_push(Key),
+ Blocks = push_messages(State, Msgs),
+ %% Decrypt & Verify
+ DState = enacl:secretstream_xchacha20poly1305_init_pull(Header, Key),
+ pull_messages(DState, Blocks, Msgs)
+ end).
+
+%% HELPERS
+
%% INTERNAL FUNCTIONS
%% ------------------------------------------------------------
+
badargs(Thunk) ->
try
Thunk(),
diff --git a/eqc_test/enacl_ext_eqc.erl b/eqc_test/enacl_ext_eqc.erl
index 8174f43..8eced40 100644
--- a/eqc_test/enacl_ext_eqc.erl
+++ b/eqc_test/enacl_ext_eqc.erl
@@ -1,7 +1,8 @@
-module(enacl_ext_eqc).
-include_lib("eqc/include/eqc.hrl").
--compile(export_all).
+-compile({parse_transform, eqc_parallelize}).
+-compile([export_all, nowarn_export_all]).
public_keypair() ->
?LET(#{ public := PK, secret := SK}, enacl_ext:curve25519_keypair(),
diff --git a/rebar.config b/rebar.config
index 36c287a..e9d0ca5 100644
--- a/rebar.config
+++ b/rebar.config
@@ -2,6 +2,8 @@
{plugins, [pc]}.
+{project_plugins, [rebar3_hex]}.
+
{provider_hooks, [
{pre, [
{compile, {pc, compile}},
diff --git a/shell.nix b/shell.nix
new file mode 100644
index 0000000..c252f46
--- /dev/null
+++ b/shell.nix
@@ -0,0 +1,12 @@
+{ pkgs ? import {} }:
+
+pkgs.mkShell {
+ buildInputs = [
+ pkgs.hello
+
+ # keep this line if you use bash
+ pkgs.bashInteractive
+ pkgs.erlang
+ pkgs.libsodium
+ ];
+}
diff --git a/src/enacl.app.src b/src/enacl.app.src
index d8c57e8..8c381cd 100644
--- a/src/enacl.app.src
+++ b/src/enacl.app.src
@@ -1,6 +1,6 @@
{application,enacl,
[{description,"Erlang libsodium (NaCl) bindings"},
- {vsn,"0.17.2"},
+ {vsn,"1.2.1"},
{registered,[]},
{applications,[kernel,stdlib]},
{env,[]},
diff --git a/src/enacl.erl b/src/enacl.erl
index 15ea2c2..378bb7b 100644
--- a/src/enacl.erl
+++ b/src/enacl.erl
@@ -8,11 +8,14 @@
%%% portable variant of the NaCl library. The C-level API is interchangeable so we can run
%%% on any of these underlying libraries as seen from the Erlang world. We simply have to
%%% restrict ourselves to the portion of the code base which is overlapping.
-%%% Warning: The cryptographic strength of your implementation is no stronger than
-%%% plaintext cryptography unless you take care in using these primitives correctly. Hence,
-%%% implementors should use these primitives with that in mind.
+%%% Warning: It is necessary to apply the primitives here correctly. Wrong
+%%% application may result in severely reduced strength of the cryptography. Take some
+%%% time to make sure this is the case before using.
%%% Note: All functions will fail with a `badarg' error if given incorrect
-%%% parameters.
+%%% parameters. Also, if something is wrong internally, they will raise an error of
+%%% the form `enacl_internal_error'. There is usually no way to continue gracefully
+%%% from either of these. A third error is `enacl_finalized', raised when you try
+%%% re-using an already finalized state for multi-part messages.
%%% @end.
-module(enacl).
@@ -25,21 +28,26 @@
box_beforenm/2,
box_afternm/3,
box_open_afternm/3,
- box_nonce_size/0,
- box_public_key_bytes/0,
- box_secret_key_bytes/0,
- box_beforenm_bytes/0,
+ box_NONCEBYTES/0,
+ box_PUBLICKEYBYTES/0,
+ box_SECRETKEYBYTES/0,
+ box_BEFORENMBYTES/0,
- %% EQC
- sign_keypair_public_size/0,
- sign_keypair_secret_size/0,
+ sign_PUBLICBYTES/0,
+ sign_SECRETBYTES/0,
+ sign_SEEDBYTES/0,
sign_keypair/0,
+ sign_seed_keypair/1,
sign/2,
sign_open/2,
sign_detached/2,
sign_verify_detached/3,
- %% EQC
+ sign_init/0,
+ sign_update/2,
+ sign_final_create/2,
+ sign_final_verify/3,
+
box_seal/2,
box_seal_open/3
]).
@@ -47,42 +55,49 @@
%% Secret key crypto
-export([
%% EQC
- secretbox_key_size/0,
- secretbox_nonce_size/0,
+ secretbox_KEYBYTES/0,
+ secretbox_NONCEBYTES/0,
secretbox/3,
secretbox_open/3,
%% No Tests!
- stream_chacha20_key_size/0,
- stream_chacha20_nonce_size/0,
+ stream_chacha20_KEYBYTES/0,
+ stream_chacha20_NONCEBYTES/0,
stream_chacha20/3,
stream_chacha20_xor/3,
%% EQC
- aead_chacha20poly1305_encrypt/4,
- aead_chacha20poly1305_decrypt/4,
- aead_chacha20poly1305_KEYBYTES/0,
- aead_chacha20poly1305_NONCEBYTES/0,
- aead_chacha20poly1305_ABYTES/0,
- aead_chacha20poly1305_MESSAGEBYTES_MAX/0,
+ aead_chacha20poly1305_ietf_encrypt/4,
+ aead_chacha20poly1305_ietf_decrypt/4,
+ aead_chacha20poly1305_ietf_KEYBYTES/0,
+ aead_chacha20poly1305_ietf_NPUBBYTES/0,
+ aead_chacha20poly1305_ietf_ABYTES/0,
+ aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX/0,
+
+ aead_xchacha20poly1305_ietf_encrypt/4,
+ aead_xchacha20poly1305_ietf_decrypt/4,
+ aead_xchacha20poly1305_ietf_KEYBYTES/0,
+ aead_xchacha20poly1305_ietf_NPUBBYTES/0,
+ aead_xchacha20poly1305_ietf_ABYTES/0,
+ aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX/0,
%% EQC
- stream_key_size/0,
- stream_nonce_size/0,
+ stream_KEYBYTES/0,
+ stream_NONCEBYTES/0,
stream/3,
%% No Tests!
stream_xor/3,
%% EQC
- auth_key_size/0,
- auth_size/0,
+ auth_KEYBYTES/0,
+ auth_BYTES/0,
auth/2,
auth_verify/3,
%% EQC
- onetime_auth_key_size/0,
- onetime_auth_size/0,
+ onetime_auth_KEYBYTES/0,
+ onetime_auth_BYTES/0,
onetime_auth/2,
onetime_auth_verify/3
]).
@@ -96,25 +111,35 @@
generichash_update/2,
generichash_final/1,
- %% No Tests!
+ %% EQC!
shorthash_key_size/0,
shorthash_size/0,
shorthash/2,
- %% EQC
+ pwhash_SALTBYTES/0,
pwhash/2,
+ pwhash/4,
+ pwhash/5,
pwhash_str/1,
+ pwhash_str/3,
pwhash_str_verify/2
]).
+%% Key derivation
+-export([
+ kdf_KEYBYTES/0,
+ kdf_CONTEXTBYTES/0,
+ kdf_derive_from_key/3
+]).
+
%% Low-level subtle functions which are hard to get correct
-export([
%% EQC
hash/1,
verify_16/2,
verify_32/2,
-
+
%% No Tests!
unsafe_memzero/1
]).
@@ -122,7 +147,9 @@
%% Randomness
-export([
%% EQC
- randombytes/1
+ randombytes/1,
+ randombytes_uint32/0,
+ randombytes_uniform/1
]).
%%% Specific primitives
@@ -137,6 +164,7 @@
-export([
%% No Tests!
crypto_sign_ed25519_keypair/0,
+ crypto_sign_ed25519_sk_to_pk/1,
crypto_sign_ed25519_public_to_curve25519/1,
crypto_sign_ed25519_secret_to_curve25519/1,
crypto_sign_ed25519_public_size/0,
@@ -145,18 +173,53 @@
%% Key exchange functions
-export([
- %% No Tests!
+ %% EQC
kx_keypair/0,
kx_client_session_keys/3,
kx_server_session_keys/3,
- kx_public_key_size/0,
- kx_secret_key_size/0,
- kx_session_key_size/0
+ kx_PUBLICKEYBYTES/0,
+ kx_SECRETKEYBYTES/0,
+ kx_SESSIONKEYBYTES/0
]).
+%% Secretstream operations.
+-export([
+ %% Unit tests
+ secretstream_xchacha20poly1305_ABYTES/0,
+ secretstream_xchacha20poly1305_HEADERBYTES/0,
+ secretstream_xchacha20poly1305_KEYBYTES/0,
+ secretstream_xchacha20poly1305_MESSAGEBYTES_MAX/0,
+ secretstream_xchacha20poly1305_TAG_MESSAGE/0,
+ secretstream_xchacha20poly1305_TAG_PUSH/0,
+ secretstream_xchacha20poly1305_TAG_REKEY/0,
+ secretstream_xchacha20poly1305_TAG_FINAL/0,
+ secretstream_xchacha20poly1305_keygen/0,
+ secretstream_xchacha20poly1305_init_push/1,
+ secretstream_xchacha20poly1305_push/4,
+ secretstream_xchacha20poly1305_init_pull/2,
+ secretstream_xchacha20poly1305_pull/3,
+ secretstream_xchacha20poly1305_rekey/1
+ ]).
+
%% Internal verification of the system
-export([verify/0]).
+%% Type specifications
+-type generichash_bytes() :: 10..64.
+-type sign_state() :: reference().
+
+-type pwhash_alg() :: default | argon2i13 | argon2id13 | pos_integer().
+-type pwhash_limit() :: interactive | moderate | sensitive | pos_integer().
+-type secretstream_xchacha20poly1305_tag() :: message | rekey | final | push | pos_integer().
+
+-export_type([
+ generichash_bytes/0,
+ pwhash_alg/0,
+ pwhash_limit/0,
+ secretstream_xchacha20poly1305_tag/0,
+ sign_state/0
+]).
+
%% Definitions of system budgets
@@ -172,9 +235,9 @@
-define(SECRETBOX_OPEN_REDUCTIONS, 17 * 2).
-define(STREAM_SIZE, 16 * 1024).
-define(STREAM_REDUCTIONS, 17 * 2).
--define(AUTH_SIZE, 4 * 1024).
+-define(auth_BYTES, 4 * 1024).
-define(AUTH_REDUCTIONS, 17 * 2).
--define(ONETIME_AUTH_SIZE, 16 * 1024).
+-define(ONETIME_auth_BYTES, 16 * 1024).
-define(ONETIME_AUTH_REDUCTIONS, 16 * 2).
-define(ED25519_PUBLIC_TO_CURVE_REDS, 20 * 2).
-define(ED25519_SECRET_TO_CURVE_REDS, 20 * 2).
@@ -204,6 +267,14 @@
-define(CRYPTO_GENERICHASH_KEYBYTES_MAX, 64).
-define(CRYPTO_GENERICHASH_KEYBYTES, 32).
+-define(CRYPTO_SECRETSTREAM_TAG_MESSAGE, 0).
+-define(CRYPTO_SECRETSTREAM_TAG_PUSH, 1).
+-define(CRYPTO_SECRETSTREAM_TAG_REKEY, 2).
+-define(CRYPTO_SECRETSTREAM_TAG_FINAL, 3).
+
+%% Size limits
+-define(MAX_32BIT_INT, 1 bsl 32).
+
%% @doc Verify makes sure the constants defined in libsodium matches ours
verify() ->
true = equals(binary:copy(<<0>>, enacl_nif:crypto_box_ZEROBYTES()), ?P_ZEROBYTES),
@@ -230,7 +301,11 @@ verify() ->
{crypto_generichash_BYTES_MAX, ?CRYPTO_GENERICHASH_BYTES_MAX},
{crypto_generichash_KEYBYTES, ?CRYPTO_GENERICHASH_KEYBYTES},
{crypto_generichash_KEYBYTES_MIN, ?CRYPTO_GENERICHASH_KEYBYTES_MIN},
- {crypto_generichash_KEYBYTES_MAX, ?CRYPTO_GENERICHASH_KEYBYTES_MAX}
+ {crypto_generichash_KEYBYTES_MAX, ?CRYPTO_GENERICHASH_KEYBYTES_MAX},
+ {crypto_secretstream_xchacha20poly1305_TAG_MESSAGE, ?CRYPTO_SECRETSTREAM_TAG_MESSAGE},
+ {crypto_secretstream_xchacha20poly1305_TAG_PUSH, ?CRYPTO_SECRETSTREAM_TAG_PUSH},
+ {crypto_secretstream_xchacha20poly1305_TAG_REKEY, ?CRYPTO_SECRETSTREAM_TAG_REKEY},
+ {crypto_secretstream_xchacha20poly1305_TAG_FINAL, ?CRYPTO_SECRETSTREAM_TAG_FINAL}
],
run_verifiers(Verifiers).
@@ -312,8 +387,7 @@ unsafe_memzero(_) ->
%% This function generates a hash of the message using a key. The hash size is
%% either 16, 32 or 64 bytes
%% @end
--type generichash_bytes() :: 10..64.
--spec generichash(generichash_bytes(), iodata(), binary()) -> {ok, binary()} | {error, term()}.
+-spec generichash(generichash_bytes(), iodata(), binary()) -> binary().
generichash(HashSize, Message, Key) ->
enacl_nif:crypto_generichash(HashSize, Message, Key).
@@ -322,40 +396,92 @@ generichash(HashSize, Message, Key) ->
%% This function generates a hash of the message. The hash size is
%% either 16, 32 or 64 bytes
%% @end
--spec generichash(generichash_bytes(), iodata()) -> {ok, binary()} | {error, term()}.
+-spec generichash(generichash_bytes(), iodata()) -> binary().
generichash(HashSize, Message) ->
enacl_nif:crypto_generichash(HashSize, Message, <<>>).
+%% @doc generichash_init/2 initializes a multi-part hash.
+%% @end
+-spec generichash_init(generichash_bytes(), binary()) -> reference().
generichash_init(HashSize, Key) ->
enacl_nif:crypto_generichash_init(HashSize, Key).
-generichash_update({hashstate, HashSize, HashState}, Message) ->
- enacl_nif:crypto_generichash_update(HashSize, HashState, Message).
+%% @doc generichash_update/2 updates a multi-part hash with new data.
+%% @end
+-spec generichash_update(reference(), iodata()) -> reference().
+generichash_update(State, Message) ->
+ enacl_nif:crypto_generichash_update(State, Message).
-generichash_final({hashstate, HashSize, HashState}) ->
- enacl_nif:crypto_generichash_final(HashSize, HashState).
+%% @doc generichash_final/1 finalizes a multi-part hash.
+-spec generichash_final(reference()) -> binary().
+generichash_final(State) ->
+ enacl_nif:crypto_generichash_final(State).
+%% @doc pwhash_SALTBYTES/0 returns the number of bytes required for salt.
+%% @end
+-spec pwhash_SALTBYTES() -> pos_integer().
+pwhash_SALTBYTES() ->
+ enacl_nif:crypto_pwhash_SALTBYTES().
%% @doc pwhash/2 hash a password
%%
%% This function generates a fixed size salted hash of a user defined password.
+%% Defaults to interactive/interactive limits.
%% @end
--spec pwhash(iodata(), binary()) -> {ok, binary()} | {error, term()}.
+-spec pwhash(iodata(), binary()) -> binary().
pwhash(Password, Salt) ->
- enacl_nif:crypto_pwhash(Password, Salt).
+ pwhash(Password, Salt, interactive, interactive).
+
+%% @doc pwhash/4 hash a password
+%%
+%% This function generates a fixed size salted hash of a user defined password given Ops and Mem
+%% limits.
+%% @end
+-spec pwhash(Password, Salt, Ops, Mem) -> binary()
+ when
+ Password :: iodata(),
+ Salt :: binary(),
+ Ops :: pwhash_limit(),
+ Mem :: pwhash_limit().
+pwhash(Password, Salt, Ops, Mem) ->
+ enacl_nif:crypto_pwhash(Password, Salt, Ops, Mem, default).
+
+%% @doc pwhash/5 hash a password
+%%
+%% This function generates a fixed size salted hash of a user defined password given Ops and Mem
+%% limits.
+%% @end
+-spec pwhash(Password, Salt, Ops, Mem, Alg) -> binary()
+ when
+ Password :: iodata(),
+ Salt :: binary(),
+ Ops :: pwhash_limit(),
+ Mem :: pwhash_limit(),
+ Alg :: pwhash_alg().
+pwhash(Password, Salt, Ops, Mem, Alg) ->
+ enacl_nif:crypto_pwhash(Password, Salt, Ops, Mem, Alg).
%% @doc pwhash_str/1 generates a ASCII encoded hash of a password
%%
%% This function generates a fixed size, salted, ASCII encoded hash of a user defined password.
+%% Defaults to interactive/interactive limits.
%% @end
--spec pwhash_str(iodata()) -> {ok, iodata()} | {error, term()}.
+-spec pwhash_str(iodata()) -> iodata().
pwhash_str(Password) ->
- case enacl_nif:crypto_pwhash_str(Password) of
- {ok, ASCII} ->
- {ok, strip_null_terminate(ASCII)};
- {error, Reason} ->
- {error, Reason}
- end.
+ pwhash_str(Password, interactive, interactive).
+
+%% @doc pwhash_str/3 generates a ASCII encoded hash of a password
+%%
+%% This function generates a fixed size, salted, ASCII encoded hash of a user defined password
+%% given Ops and Mem limits.
+%% @end
+-spec pwhash_str(Password, Ops, Mem) -> iodata()
+ when
+ Password :: iodata(),
+ Ops :: pwhash_limit(),
+ Mem :: pwhash_limit().
+pwhash_str(Password, Ops, Mem) ->
+ strip_null_terminate(enacl_nif:crypto_pwhash_str(Password, Ops, Mem)).
strip_null_terminate(Binary) ->
[X, _] = binary:split(Binary, <<0>>),
@@ -373,6 +499,29 @@ null_terminate(ASCII) ->
pwhash_str_verify(HashPassword, Password) ->
enacl_nif:crypto_pwhash_str_verify(null_terminate(HashPassword), Password).
+%% Key Derivation
+%% @doc kdf_KEYBYTES/0 returns the number of bytes required for master key.
+%% @end
+-spec kdf_KEYBYTES() -> pos_integer().
+kdf_KEYBYTES() ->
+ enacl_nif:crypto_kdf_KEYBYTES().
+
+%% @doc kdf_CONTEXTBYTES/0 returns the number of bytes required for context.
+%% @end
+-spec kdf_CONTEXTBYTES() -> pos_integer().
+kdf_CONTEXTBYTES() ->
+ enacl_nif:crypto_kdf_CONTEXTBYTES().
+
+%% @doc kdf_derive_from_key/3 derive a key from a single high entropy key
+%% @end.
+-spec kdf_derive_from_key(MasterKey, Context, Id) -> binary()
+ when
+ MasterKey :: iodata(),
+ Context :: binary(),
+ Id :: pos_integer().
+kdf_derive_from_key(MasterKey, Context, Id) ->
+ enacl_nif:crypto_kdf_derive_from_key(MasterKey, Context, Id).
+
%% Public Key Crypto
%% ---------------------
%% @doc box_keypair/0 creates a new Public/Secret keypair.
@@ -415,10 +564,7 @@ box(Msg, Nonce, PK, SK) ->
SK :: binary(),
Msg :: binary().
box_open(CipherText, Nonce, PK, SK) ->
- case enacl_nif:crypto_box_open([?P_BOXZEROBYTES, CipherText], Nonce, PK, SK) of
- {error, Err} -> {error, Err};
- Bin when is_binary(Bin) -> {ok, Bin}
- end.
+ enacl_nif:crypto_box_open([?P_BOXZEROBYTES, CipherText], Nonce, PK, SK).
%% @doc box_beforenm/2 precomputes a box shared key for a PK/SK keypair
%% @end
@@ -467,52 +613,43 @@ box_afternm(Msg, Nonce, Key) ->
box_open_afternm(CipherText, Nonce, Key) ->
case iolist_size(CipherText) of
K when K =< ?BOX_AFTERNM_SIZE ->
- R =
- case enacl_nif:crypto_box_open_afternm_b(
- [?P_BOXZEROBYTES, CipherText], Nonce, Key) of
- {error, Err} ->
- {error, Err};
- Bin when is_binary(Bin) ->
- {ok, Bin}
- end,
+ R = enacl_nif:crypto_box_open_afternm_b([?P_BOXZEROBYTES, CipherText], Nonce, Key),
bump(R, ?BOX_AFTERNM_REDUCTIONS, ?BOX_AFTERNM_SIZE, K);
_ ->
- case enacl_nif:crypto_box_open_afternm(
- [?P_BOXZEROBYTES, CipherText], Nonce, Key) of
- {error, Err} ->
- {error, Err};
- Bin when is_binary(Bin) ->
- {ok, Bin}
- end
+ enacl_nif:crypto_box_open_afternm([?P_BOXZEROBYTES, CipherText], Nonce, Key)
end.
-%% @doc box_nonce_size/0 return the byte-size of the nonce
+%% @doc box_NONCEBYTES()/0 return the byte-size of the nonce
%%
%% Used to obtain the size of the nonce.
%% @end.
--spec box_nonce_size() -> pos_integer().
-box_nonce_size() ->
+-spec box_NONCEBYTES() -> pos_integer().
+box_NONCEBYTES() ->
enacl_nif:crypto_box_NONCEBYTES().
%% @private
--spec box_public_key_bytes() -> pos_integer().
-box_public_key_bytes() ->
+-spec box_PUBLICKEYBYTES() -> pos_integer().
+box_PUBLICKEYBYTES() ->
enacl_nif:crypto_box_PUBLICKEYBYTES().
%% @private
-box_beforenm_bytes() ->
+box_BEFORENMBYTES() ->
enacl_nif:crypto_box_BEFORENMBYTES().
%% Signatures
%% @private
-sign_keypair_public_size() ->
+sign_PUBLICBYTES() ->
enacl_nif:crypto_sign_PUBLICKEYBYTES().
%% @private
-sign_keypair_secret_size() ->
+sign_SECRETBYTES() ->
enacl_nif:crypto_sign_SECRETKEYBYTES().
+%% @private
+sign_SEEDBYTES() ->
+ enacl_nif:crypto_sign_SEEDBYTES().
+
%% @doc sign_keypair/0 returns a signature keypair for signing
%%
%% The returned value is a map in order to make it harder to misuse keys.
@@ -522,6 +659,17 @@ sign_keypair() ->
{PK, SK} = enacl_nif:crypto_sign_keypair(),
#{ public => PK, secret => SK}.
+%% @doc sign_seed_keypair/1 returns a signature keypair based on seed for signing
+%%
+%% The returned value is a map in order to make it harder to misuse keys.
+%% @end
+-spec sign_seed_keypair(S) -> #{ atom() => binary() }
+ when
+ S :: binary().
+sign_seed_keypair(S) ->
+ {PK, SK} = enacl_nif:crypto_sign_seed_keypair(S),
+ #{ public => PK, secret => SK}.
+
%% @doc sign/2 signs a message with a digital signature identified by a secret key.
%%
%% Given a message `M' and a secret key `SK' the function will sign the message and return a signed message `SM'.
@@ -546,10 +694,7 @@ sign(M, SK) ->
PK :: binary(),
M :: binary().
sign_open(SM, PK) ->
- case enacl_nif:crypto_sign_open(SM, PK) of
- M when is_binary(M) -> {ok, M};
- {error, Err} -> {error, Err}
- end.
+ enacl_nif:crypto_sign_open(SM, PK).
%% @doc sign_detached/2 computes a digital signature given a message and a secret key.
%%
@@ -567,21 +712,58 @@ sign_detached(M, SK) ->
%% message for the given public key.
%%
%% Given a signature `SIG', a message `M', and a public key `PK', the function computes
-%% true iff the `SIG' is valid for `M' and `PK'.
--spec sign_verify_detached(SIG, M, PK) -> {ok, M} | {error, failed_verification}
+%% true iff the `SIG' is valid for `M' and `PK'; false otherwise.
+-spec sign_verify_detached(SIG, M, PK) -> boolean()
when
SIG :: binary(),
M :: iodata(),
PK :: binary().
sign_verify_detached(SIG, M, PK) ->
- case enacl_nif:crypto_sign_verify_detached(SIG, M, PK) of
- true -> {ok, M};
- false -> {error, failed_verification}
- end.
+ enacl_nif:crypto_sign_verify_detached(SIG, M, PK).
+
+
+%% @doc sign_init/0 initialize a multi-part signature state.
+%%
+%% This state must be passed to all future calls to `sign_update/2',
+%% `sign_final_create/2' and `sign_final_verify/3'.
+%% @end
+-spec sign_init() -> sign_state().
+sign_init() ->
+ enacl_nif:crypto_sign_init().
+
+%% @doc sign_update/2 update the signature state `S' with a new chunk of data `M'.
+%% @end
+-spec sign_update(S, M) -> sign_state() | {error, sign_update_error}
+ when S :: sign_state(),
+ M :: iodata().
+sign_update(SignState, M) ->
+ enacl_nif:crypto_sign_update(SignState, M).
+
+
+%% @doc sign_final_create/2 computes the signature for the previously supplied
+%% message(s) using the secret key `SK'.
+%% @end
+-spec sign_final_create(S, SK) -> {ok, binary()} | {error, atom()}
+ when S :: sign_state(),
+ SK :: iodata().
+sign_final_create(SignState, SK) ->
+ enacl_nif:crypto_sign_final_create(SignState, SK).
+
+%% @doc sign_final_verify/3 verify a chunked signature
+%%
+%% Verifies that `SIG' is a valid signature for the message whose content has
+%% been previously supplied using `sign_update/2' using the public key `PK.'
+%% @end
+-spec sign_final_verify(S, SIG, PK) -> boolean()
+ when S :: sign_state(),
+ SIG :: binary(),
+ PK :: iodata().
+sign_final_verify(SignState, SIG, PK) ->
+ enacl_nif:crypto_sign_final_verify(SignState, SIG, PK).
%% @private
--spec box_secret_key_bytes() -> pos_integer().
-box_secret_key_bytes() ->
+-spec box_SECRETKEYBYTES() -> pos_integer().
+box_SECRETKEYBYTES() ->
enacl_nif:crypto_box_SECRETKEYBYTES().
%% @doc seal_box/2 encrypts an anonymous message to another party.
@@ -611,10 +793,7 @@ box_seal(Msg, PK) ->
SK :: binary(),
Msg :: binary().
box_seal_open(SealedCipherText, PK, SK) ->
- case enacl_nif:crypto_box_seal_open(SealedCipherText, PK, SK) of
- {error, Err} -> {error, Err};
- Bin when is_binary(Bin) -> {ok, Bin}
- end.
+ enacl_nif:crypto_box_seal_open(SealedCipherText, PK, SK).
%% @doc secretbox/3 encrypts a message with a key
%%
@@ -651,43 +830,37 @@ secretbox(Msg, Nonce, Key) ->
secretbox_open(CipherText, Nonce, Key) ->
case iolist_size(CipherText) of
K when K =< ?SECRETBOX_SIZE ->
- R = case enacl_nif:crypto_secretbox_open_b([?S_BOXZEROBYTES, CipherText],
- Nonce, Key) of
- {error, Err} -> {error, Err};
- Bin when is_binary(Bin) -> {ok, Bin}
- end,
+ R = enacl_nif:crypto_secretbox_open_b([?S_BOXZEROBYTES, CipherText],
+ Nonce, Key),
bump(R, ?SECRETBOX_OPEN_REDUCTIONS, ?SECRETBOX_SIZE, K);
_ ->
- case enacl_nif:crypto_secretbox_open([?S_BOXZEROBYTES, CipherText], Nonce, Key) of
- {error, Err} -> {error, Err};
- Bin when is_binary(Bin) -> {ok, Bin}
- end
+ enacl_nif:crypto_secretbox_open([?S_BOXZEROBYTES, CipherText], Nonce, Key)
end.
-%% @doc secretbox_nonce_size/0 returns the size of the secretbox nonce
+%% @doc secretbox_NONCEBYTES()/0 returns the size of the secretbox nonce
%%
%% When encrypting with a secretbox, the nonce must have this size
%% @end
-secretbox_nonce_size() ->
+secretbox_NONCEBYTES() ->
enacl_nif:crypto_secretbox_NONCEBYTES().
-%% @doc secretbox_key_size/0 returns the size of the secretbox key
+%% @doc secretbox_KEYBYTES/0 returns the size of the secretbox key
%%
%% When encrypting with a secretbox, the key must have this size
%% @end
-secretbox_key_size() ->
+secretbox_KEYBYTES() ->
enacl_nif:crypto_secretbox_KEYBYTES().
-%% @doc stream_chacha20_nonce_size/0 returns the byte size of the nonce for streams
+%% @doc stream_chacha20_NONCEBYTES/0 returns the byte size of the nonce for streams
%% @end
--spec stream_chacha20_nonce_size() -> ?CRYPTO_STREAM_CHACHA20_NONCEBYTES.
-stream_chacha20_nonce_size() ->
+-spec stream_chacha20_NONCEBYTES() -> ?CRYPTO_STREAM_CHACHA20_NONCEBYTES.
+stream_chacha20_NONCEBYTES() ->
?CRYPTO_STREAM_CHACHA20_NONCEBYTES.
-%% @doc stream_key_size/0 returns the byte size of the key for streams
+%% @doc stream_chacha20_KEYBYTES/0 returns the byte size of the key for streams
%% @end
--spec stream_chacha20_key_size() -> ?CRYPTO_STREAM_CHACHA20_KEYBYTES.
-stream_chacha20_key_size() ->
+-spec stream_chacha20_KEYBYTES() -> ?CRYPTO_STREAM_CHACHA20_KEYBYTES.
+stream_chacha20_KEYBYTES() ->
?CRYPTO_STREAM_CHACHA20_KEYBYTES.
%% @doc stream_chacha20/3 produces a cryptographic stream suitable for secret-key encryption
@@ -735,16 +908,16 @@ stream_chacha20_xor(Msg, Nonce, Key) ->
enacl_nif:crypto_stream_chacha20_xor(Msg, Nonce, Key)
end.
-%% @doc stream_nonce_size/0 returns the byte size of the nonce for streams
+%% @doc stream_NONCEBYTES/0 returns the byte size of the nonce for streams
%% @end
--spec stream_nonce_size() -> ?CRYPTO_STREAM_NONCEBYTES.
-stream_nonce_size() ->
+-spec stream_NONCEBYTES() -> ?CRYPTO_STREAM_NONCEBYTES.
+stream_NONCEBYTES() ->
?CRYPTO_STREAM_NONCEBYTES.
-%% @doc stream_key_size/0 returns the byte size of the key for streams
+%% @doc stream_KEYBYTES/0 returns the byte size of the key for streams
%% @end
--spec stream_key_size() -> ?CRYPTO_STREAM_KEYBYTES.
-stream_key_size() ->
+-spec stream_KEYBYTES() -> ?CRYPTO_STREAM_KEYBYTES.
+stream_KEYBYTES() ->
?CRYPTO_STREAM_KEYBYTES.
%% @doc stream/3 produces a cryptographic stream suitable for secret-key encryption
@@ -792,16 +965,16 @@ stream_xor(Msg, Nonce, Key) ->
enacl_nif:crypto_stream_xor(Msg, Nonce, Key)
end.
-%% @doc auth_key_size/0 returns the byte-size of the authentication key
+%% @doc auth_KEYBYTES/0 returns the byte-size of the authentication key
%% @end
--spec auth_key_size() -> pos_integer().
-auth_key_size() ->
+-spec auth_KEYBYTES() -> pos_integer().
+auth_KEYBYTES() ->
enacl_nif:crypto_auth_KEYBYTES().
-%% @doc auth_size/0 returns the byte-size of the authenticator
+%% @doc auth_BYTES/0 returns the byte-size of the authenticator
%% @end
--spec auth_size() -> pos_integer().
-auth_size() ->
+-spec auth_BYTES() -> pos_integer().
+auth_BYTES() ->
enacl_nif:crypto_auth_BYTES().
%% @doc auth/2 produces an authenticator (MAC) for a message
@@ -816,8 +989,8 @@ auth_size() ->
Authenticator :: binary().
auth(Msg, Key) ->
case iolist_size(Msg) of
- K when K =< ?AUTH_SIZE ->
- bump(enacl_nif:crypto_auth_b(Msg, Key), ?AUTH_REDUCTIONS, ?AUTH_SIZE, K);
+ K when K =< ?auth_BYTES ->
+ bump(enacl_nif:crypto_auth_b(Msg, Key), ?AUTH_REDUCTIONS, ?auth_BYTES, K);
_ ->
enacl_nif:crypto_auth(Msg, Key)
end.
@@ -834,10 +1007,10 @@ auth(Msg, Key) ->
Key :: binary().
auth_verify(A, M, K) ->
case iolist_size(M) of
- K when K =< ?AUTH_SIZE ->
+ K when K =< ?auth_BYTES ->
bump(enacl_nif:crypto_auth_verify_b(A, M, K),
?AUTH_REDUCTIONS,
- ?AUTH_SIZE,
+ ?auth_BYTES,
K);
_ ->
enacl_nif:crypto_auth_verify(A, M, K)
@@ -859,6 +1032,10 @@ shorthash_size() ->
%%
%% Given a `Msg' and a `Key' produce a MAC/Authenticator for that message. The key can be reused for several such Msg/Authenticator pairs.
%% An eavesdropper will not learn anything extra about the message structure.
+%%
+%% The intended use is to generate a random key and use it as a hash table or bloom filter function.
+%% This avoids an enemy their ability to predict where a collision would occur in the data structure,
+%% since they don't know the key.
%% @end
-spec shorthash(Msg, Key) -> Authenticator
when
@@ -880,10 +1057,10 @@ shorthash(Msg, Key) ->
Authenticator :: binary().
onetime_auth(Msg, Key) ->
case iolist_size(Msg) of
- K when K =< ?ONETIME_AUTH_SIZE ->
+ K when K =< ?ONETIME_auth_BYTES ->
bump(enacl_nif:crypto_onetimeauth_b(Msg, Key),
?ONETIME_AUTH_REDUCTIONS,
- ?ONETIME_AUTH_SIZE,
+ ?ONETIME_auth_BYTES,
K);
_ ->
enacl_nif:crypto_onetimeauth(Msg, Key)
@@ -902,25 +1079,25 @@ onetime_auth(Msg, Key) ->
Key :: binary().
onetime_auth_verify(A, M, K) ->
case iolist_size(M) of
- K when K =< ?ONETIME_AUTH_SIZE ->
+ K when K =< ?ONETIME_auth_BYTES ->
bump(enacl_nif:crypto_onetimeauth_verify_b(A, M, K),
?ONETIME_AUTH_REDUCTIONS,
- ?ONETIME_AUTH_SIZE,
+ ?ONETIME_auth_BYTES,
K);
_ ->
enacl_nif:crypto_onetimeauth_verify(A, M, K)
end.
-%% @doc onetime_auth_size/0 returns the number of bytes of the one-time authenticator
+%% @doc onetime_auth_BYTES/0 returns the number of bytes of the one-time authenticator
%% @end
--spec onetime_auth_size() -> pos_integer().
-onetime_auth_size() ->
+-spec onetime_auth_BYTES() -> pos_integer().
+onetime_auth_BYTES() ->
enacl_nif:crypto_onetimeauth_BYTES().
-%% @doc onetime_auth_key_size/0 returns the byte-size of the onetime authentication key
+%% @doc onetime_auth_KEYBYTES/0 returns the byte-size of the onetime authentication key
%% @end
--spec onetime_auth_key_size() -> pos_integer().
-onetime_auth_key_size() ->
+-spec onetime_auth_KEYBYTES() -> pos_integer().
+onetime_auth_KEYBYTES() ->
enacl_nif:crypto_onetimeauth_KEYBYTES().
%% Curve 25519 Crypto
@@ -957,6 +1134,14 @@ crypto_sign_ed25519_keypair() ->
{PK, SK} = enacl_nif:crypto_sign_ed25519_keypair(),
#{ public => PK, secret => SK }.
+%% @doc crypto_sign_ed25519_sk_to_pk/1 derives an ed25519 public key from a secret key
+%% The ed25519 signatures secret keys contains enough information to dervice its corresponding
+%% public key. This function extracts the public key from the secret if needed.
+%% @end
+-spec crypto_sign_ed25519_sk_to_pk(Secret :: binary()) -> binary().
+crypto_sign_ed25519_sk_to_pk(Secret) ->
+ enacl_nif:crypto_sign_ed25519_sk_to_pk(Secret).
+
%% @doc crypto_sign_ed25519_public_to_curve25519/1 converts a given Ed 25519 public
%% key to a Curve 25519 public key.
%% @end
@@ -1026,82 +1211,299 @@ kx_server_session_keys(ServerPk, ServerSk, ClientPk) ->
{Rx, Tx} = enacl_nif:crypto_kx_server_session_keys(ServerPk, ServerSk, ClientPk),
#{ server_rx => Rx, server_tx => Tx}.
-%% @doc kx_session_key_size/0 returns the number of bytes of the generated during key exchange session key.
+%% @doc kx_SESSIONKEYBYTES/0 returns the number of bytes of the generated during key exchange session key.
%% @end
--spec kx_session_key_size() -> pos_integer().
-kx_session_key_size() ->
+-spec kx_SESSIONKEYBYTES() -> pos_integer().
+kx_SESSIONKEYBYTES() ->
enacl_nif:crypto_kx_SESSIONKEYBYTES().
-%% @doc kx_public_key_size/0 returns the number of bytes of the public key used in key exchange.
+%% @doc kx_PUBLICKEYBYTES/0 returns the number of bytes of the public key used in key exchange.
%% @end
--spec kx_public_key_size() -> pos_integer().
-kx_public_key_size() ->
+-spec kx_PUBLICKEYBYTES() -> pos_integer().
+kx_PUBLICKEYBYTES() ->
enacl_nif:crypto_kx_PUBLICKEYBYTES().
-%% @doc kx_secret_key_size/0 returns the number of bytes of the secret key used in key exchange.
+%% @doc kx_SECRETKEYBYTES/0 returns the number of bytes of the secret key used in key exchange.
%% @end
--spec kx_secret_key_size() -> pos_integer().
-kx_secret_key_size() ->
+-spec kx_SECRETKEYBYTES() -> pos_integer().
+kx_SECRETKEYBYTES() ->
enacl_nif:crypto_kx_SECRETKEYBYTES().
%% AEAD ChaCha20 Poly1305
%% ----------------------
%% @doc aead_chacha20poly1305_encrypt/4 encrypts `Message' with additional data
%% `AD' using `Key' and `Nonce'. Returns the encrypted message followed by
-%% `aead_chacha20poly1305_ABYTES/0' bytes of MAC.
+%% aead_chacha20poly1305_ABYTES/0 bytes of MAC.
%% @end
--spec aead_chacha20poly1305_encrypt(Key, Nonce, AD, Msg) -> binary() | {error, term()}
+-spec aead_chacha20poly1305_ietf_encrypt(Msg, AD, Nonce, Key) -> binary()
when Key :: binary(),
- Nonce :: pos_integer(),
+ Nonce :: binary(),
AD :: binary(),
Msg :: binary().
-aead_chacha20poly1305_encrypt(Key, Nonce, AD, Msg) ->
- NonceBin = <<0:32, Nonce:64/little-unsigned-integer>>,
- enacl_nif:crypto_aead_chacha20poly1305_encrypt(Key, NonceBin, AD, Msg).
+aead_chacha20poly1305_ietf_encrypt(Msg, AD, Nonce, Key) ->
+ enacl_nif:crypto_aead_chacha20poly1305_ietf_encrypt(Msg, AD, Nonce, Key).
%% @doc aead_chacha20poly1305_decrypt/4 decrypts ciphertext `CT' with additional
%% data `AD' using `Key' and `Nonce'. Note: `CipherText' should contain
-%% `aead_chacha20poly1305_ABYTES/0' bytes that is the MAC. Returns the decrypted
+%% aead_chacha20poly1305_ABYTES/0 bytes that is the MAC. Returns the decrypted
%% message.
%% @end
--spec aead_chacha20poly1305_decrypt(Key, Nonce, AD, CT) -> binary() | {error, term()}
+-spec aead_chacha20poly1305_ietf_decrypt(CT, AD, Nonce, Key) -> binary() | {error, term()}
when Key :: binary(),
- Nonce :: pos_integer(),
+ Nonce :: binary(),
AD :: binary(),
CT :: binary().
-aead_chacha20poly1305_decrypt(Key, Nonce, AD, CT) ->
- NonceBin = <<0:32, Nonce:64/little-unsigned-integer>>,
- enacl_nif:crypto_aead_chacha20poly1305_decrypt(Key, NonceBin, AD, CT).
+aead_chacha20poly1305_ietf_decrypt(CT, AD, Nonce, Key) ->
+ enacl_nif:crypto_aead_chacha20poly1305_ietf_decrypt(CT, AD, Nonce, Key).
%% @doc aead_chacha20poly1305_KEYBYTES/0 returns the number of bytes
%% of the key used in AEAD ChaCha20 Poly1305 encryption/decryption.
%% @end
--spec aead_chacha20poly1305_KEYBYTES() -> pos_integer().
-aead_chacha20poly1305_KEYBYTES() ->
- enacl_nif:crypto_aead_chacha20poly1305_KEYBYTES().
+-spec aead_chacha20poly1305_ietf_KEYBYTES() -> pos_integer().
+aead_chacha20poly1305_ietf_KEYBYTES() ->
+ enacl_nif:crypto_aead_chacha20poly1305_ietf_KEYBYTES().
-%% @doc aead_chacha20poly1305_NONCEBYTES/0 returns the number of bytes
+%% @doc aead_chacha20poly1305_NPUBBYTES/0 returns the number of bytes
%% of the Nonce in AEAD ChaCha20 Poly1305 encryption/decryption.
%% @end
--spec aead_chacha20poly1305_NONCEBYTES() -> pos_integer().
-aead_chacha20poly1305_NONCEBYTES() ->
- enacl_nif:crypto_aead_chacha20poly1305_NPUBBYTES().
+-spec aead_chacha20poly1305_ietf_NPUBBYTES() -> pos_integer().
+aead_chacha20poly1305_ietf_NPUBBYTES() ->
+ enacl_nif:crypto_aead_chacha20poly1305_ietf_NPUBBYTES().
%% @doc aead_chacha20poly1305_ABYTES/0 returns the number of bytes
%% of the MAC in AEAD ChaCha20 Poly1305 encryption/decryption.
%% @end
--spec aead_chacha20poly1305_ABYTES() -> pos_integer().
-aead_chacha20poly1305_ABYTES() ->
- enacl_nif:crypto_aead_chacha20poly1305_ABYTES().
+-spec aead_chacha20poly1305_ietf_ABYTES() -> pos_integer().
+aead_chacha20poly1305_ietf_ABYTES() ->
+ enacl_nif:crypto_aead_chacha20poly1305_ietf_ABYTES().
%% @doc aead_chacha20poly1305_MESSAGEBYTES_MAX/0 returns the max number of bytes
%% allowed in a message in AEAD ChaCha20 Poly1305 encryption/decryption.
%% @end
--spec aead_chacha20poly1305_MESSAGEBYTES_MAX() -> pos_integer().
-aead_chacha20poly1305_MESSAGEBYTES_MAX() ->
- enacl_nif:crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX().
+-spec aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX() -> pos_integer().
+aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX() ->
+ enacl_nif:crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX().
+
+%% AEAD XChaCha20 Poly1305
+%% ----------------------
+%% @doc aead_xchacha20poly1305_encrypt/4 encrypts `Message' with additional data
+%% `AD' using `Key' and `Nonce'. Returns the encrypted message followed by
+%% `aead_xchacha20poly1305_ABYTES/0' bytes of MAC.
+%% @end
+-spec aead_xchacha20poly1305_ietf_encrypt(Msg, AD, Nonce, Key) -> binary()
+ when Key :: binary(),
+ Nonce :: binary(),
+ AD :: binary(),
+ Msg :: binary().
+aead_xchacha20poly1305_ietf_encrypt(Msg, AD, Nonce, Key) ->
+ enacl_nif:crypto_aead_xchacha20poly1305_ietf_encrypt(Msg, AD, Nonce, Key).
+
+%% @doc aead_xchacha20poly1305_decrypt/4 decrypts ciphertext `CT' with additional
+%% data `AD' using `Key' and `Nonce'. Note: `CipherText' should contain
+%% `aead_xchacha20poly1305_ABYTES/0' bytes that is the MAC. Returns the decrypted
+%% message.
+%% @end
+-spec aead_xchacha20poly1305_ietf_decrypt(CT, AD, Nonce, Key) -> binary() | {error, term()}
+ when Key :: binary(),
+ Nonce :: binary(),
+ AD :: binary(),
+ CT :: binary().
+aead_xchacha20poly1305_ietf_decrypt(CT, AD, Nonce, Key) ->
+ enacl_nif:crypto_aead_xchacha20poly1305_ietf_decrypt(CT, AD, Nonce, Key).
+
+%% @doc aead_xchacha20poly1305_KEYBYTES/0 returns the number of bytes
+%% of the key used in AEAD XChaCha20 Poly1305 encryption/decryption.
+%% @end
+-spec aead_xchacha20poly1305_ietf_KEYBYTES() -> pos_integer().
+aead_xchacha20poly1305_ietf_KEYBYTES() ->
+ enacl_nif:crypto_aead_xchacha20poly1305_ietf_KEYBYTES().
+
+%% @doc aead_xchacha20poly1305_NPUBBYTES/0 returns the number of bytes
+%% of the Nonce in AEAD XChaCha20 Poly1305 encryption/decryption.
+%% @end
+-spec aead_xchacha20poly1305_ietf_NPUBBYTES() -> pos_integer().
+aead_xchacha20poly1305_ietf_NPUBBYTES() ->
+ enacl_nif:crypto_aead_xchacha20poly1305_ietf_NPUBBYTES().
+
+%% @doc aead_xchacha20poly1305_ABYTES/0 returns the number of bytes
+%% of the MAC in AEAD XChaCha20 Poly1305 encryption/decryption.
+%% @end
+-spec aead_xchacha20poly1305_ietf_ABYTES() -> pos_integer().
+aead_xchacha20poly1305_ietf_ABYTES() ->
+ enacl_nif:crypto_aead_xchacha20poly1305_ietf_ABYTES().
+
+%% @doc aead_xchacha20poly1305_MESSAGEBYTES_MAX/0 returns the max number of bytes
+%% allowed in a message in AEAD XChaCha20 Poly1305 encryption/decryption.
+%% @end
+-spec aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX() -> pos_integer().
+aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX() ->
+ enacl_nif:crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX().
+
+%% Secretstream
+%% ----------------------
+%% @doc secretstream_xchacha20poly1305_ABYTES/0 returns the number of bytes
+%% of the MAC used on secretstream encryption/decryption
+%% @end
+-spec secretstream_xchacha20poly1305_ABYTES() -> pos_integer().
+secretstream_xchacha20poly1305_ABYTES() ->
+ enacl_nif:crypto_secretstream_xchacha20poly1305_ABYTES().
+
+%% @doc secretstream_xchacha20poly1305_HEADERBYTES/0 returns the number
+%% of bytes for header used in secretstream encryption/decryption.
+%% @end
+-spec secretstream_xchacha20poly1305_HEADERBYTES() -> pos_integer().
+secretstream_xchacha20poly1305_HEADERBYTES() ->
+ enacl_nif:crypto_secretstream_xchacha20poly1305_HEADERBYTES().
+
+%% @doc secretstream_xchacha20poly1305_KEYBYTES/0 returns the number
+%% of bytes of the key used in secretstream encryption/decryption.
+%% @end
+-spec secretstream_xchacha20poly1305_KEYBYTES() -> pos_integer().
+secretstream_xchacha20poly1305_KEYBYTES() ->
+ enacl_nif:crypto_secretstream_xchacha20poly1305_KEYBYTES().
+
+%% @doc secretstream_xchacha20poly1305_MESSAGEBYTES_MAX/0 returns the max
+%% number of bytes allowed in a message in secretstream encryption/decryption.
+%% @end
+-spec secretstream_xchacha20poly1305_MESSAGEBYTES_MAX() -> pos_integer().
+secretstream_xchacha20poly1305_MESSAGEBYTES_MAX() ->
+ enacl_nif:crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX().
+
+%% @doc secretstream_xchacha20poly1305_TAG_MESSAGE/0 returns integer value
+%% of tag `message'. The most common tag, that doesn't add any information
+%% about the nature of the message.
+%% @end
+-spec secretstream_xchacha20poly1305_TAG_MESSAGE() -> pos_integer().
+secretstream_xchacha20poly1305_TAG_MESSAGE() ->
+ enacl_nif:crypto_secretstream_xchacha20poly1305_TAG_MESSAGE().
+
+%% @doc secretstream_xchacha20poly1305_TAG_PUSH/0 returns integer value
+%% of tag `push'.
+%%
+%% This tag indicates that the message marks the end
+%% of a set of messages, but not the end of the stream.
+%%
+%% For example, a huge JSON string sent as multiple chunks can use
+%% this tag to indicate to the application that the string is complete
+%% and that it can be decoded. But the stream itself is not closed,
+%% and more data may follow.
+%% @end
+-spec secretstream_xchacha20poly1305_TAG_PUSH() -> pos_integer().
+secretstream_xchacha20poly1305_TAG_PUSH() ->
+ enacl_nif:crypto_secretstream_xchacha20poly1305_TAG_PUSH().
+
+%% @doc secretstream_xchacha20poly1305_TAG_REKEY/0 returns integer value
+%% of tag `rekey'. Indicates that next messages will derive new keys.
+%% @end
+-spec secretstream_xchacha20poly1305_TAG_REKEY() -> pos_integer().
+secretstream_xchacha20poly1305_TAG_REKEY() ->
+ enacl_nif:crypto_secretstream_xchacha20poly1305_TAG_REKEY().
+
+%% @doc secretstream_xchacha20poly1305_TAG_FINAL/0 returns integer value
+%% of tag `final'. Indicates that the message is the last message in
+%% the secretstream.
+%% @end
+-spec secretstream_xchacha20poly1305_TAG_FINAL() -> pos_integer().
+secretstream_xchacha20poly1305_TAG_FINAL() ->
+ enacl_nif:crypto_secretstream_xchacha20poly1305_TAG_FINAL().
+
+%% @doc secretstream_xchacha20poly1305_keygen/0 returns new random key
+%% for secretsteam encryption.
+%% @end
+-spec secretstream_xchacha20poly1305_keygen() -> binary().
+secretstream_xchacha20poly1305_keygen() ->
+ enacl_nif:crypto_secretstream_xchacha20poly1305_keygen().
+
+%% @doc secretstream_xchacha20poly1305_init_push/1
+%% initializes a secretstream encryption context using given `key'.
+%% Returns `Header' and reference to encryption context.
+%% @end
+-spec secretstream_xchacha20poly1305_init_push(Key) -> {binary(), reference()}
+ when Key :: binary().
+secretstream_xchacha20poly1305_init_push(Key) ->
+ enacl_nif:crypto_secretstream_xchacha20poly1305_init_push(Key).
+
+%% @doc secretstream_xchacha20poly1305_push/4 returns encrypted chunk binary.
+%% Updates a secretstream context referenced by `Ref' with `Message' data,
+%% given `Tag' and additional data `AD'.
+%% @end
+-spec secretstream_xchacha20poly1305_push(Ref, Message, AD, Tag) -> binary()
+ when
+ Ref :: reference(),
+ Message :: binary(),
+ AD :: binary(),
+ Tag :: secretstream_xchacha20poly1305_tag().
+secretstream_xchacha20poly1305_push(Ref, Message, AD, Tag) ->
+ TagValue = secretstream_xchacha20poly1305_tag_value(Tag),
+
+ enacl_nif:crypto_secretstream_xchacha20poly1305_push(Ref, Message, AD, TagValue).
+
+%% @doc secretstream_xchacha20poly1305_init_pull/3
+%% initializes a secretstream decryption context using `Header' and `Key'.
+%% Returns reference to decryption context.
+%% @end
+-spec secretstream_xchacha20poly1305_init_pull(Header, Key) -> reference()
+ when
+ Header :: binary(),
+ Key :: binary().
+secretstream_xchacha20poly1305_init_pull(Header, Key) ->
+ enacl_nif:crypto_secretstream_xchacha20poly1305_init_pull(Header, Key).
+
+%% @doc secretstream_xchacha20poly1305_pull/3 decrypts `CipherText'
+%% with additional data `AD' in referenced decryption context `Ref'.
+%% @end
+-spec secretstream_xchacha20poly1305_pull(Ref, CipherText, AD) ->
+ {binary(), secretstream_xchacha20poly1305_tag()} | {error, failed_verification}
+ when
+ Ref :: reference(),
+ CipherText :: binary(),
+ AD :: binary().
+secretstream_xchacha20poly1305_pull(Ref, CipherText, AD) ->
+ {Message, TagValue} = enacl_nif:crypto_secretstream_xchacha20poly1305_pull(Ref, CipherText, AD),
+ {Message, secretstream_xchacha20poly1305_tag(TagValue)}.
+
+%% @doc secretstream_xchacha20poly1305_rekey/1 updates encryption/decryption context state.
+%% This doesn't add any information about key update to stream.
+%% If this function is used to create an encrypted stream,
+%% the decryption process must call that function at the exact same stream location.
+%% @end
+-spec secretstream_xchacha20poly1305_rekey(Ref) -> ok
+ when Ref :: reference().
+secretstream_xchacha20poly1305_rekey(Ref) ->
+ enacl_nif:crypto_secretstream_xchacha20poly1305_rekey(Ref).
+
+%% @doc secretstream_xchacha20poly1305_tag_value/1 returns integer value of tag.
+%% @end
+-spec secretstream_xchacha20poly1305_tag_value(TagName) -> pos_integer()
+ when TagName :: secretstream_xchacha20poly1305_tag().
+secretstream_xchacha20poly1305_tag_value(message) ->
+ enacl_nif:crypto_secretstream_xchacha20poly1305_TAG_MESSAGE();
+secretstream_xchacha20poly1305_tag_value(rekey) ->
+ enacl_nif:crypto_secretstream_xchacha20poly1305_TAG_REKEY();
+secretstream_xchacha20poly1305_tag_value(push) ->
+ enacl_nif:crypto_secretstream_xchacha20poly1305_TAG_PUSH();
+secretstream_xchacha20poly1305_tag_value(final) ->
+ enacl_nif:crypto_secretstream_xchacha20poly1305_TAG_FINAL();
+secretstream_xchacha20poly1305_tag_value(Other) ->
+ Other.
+
+%% @doc secretstream_xchacha20poly1305_tag/1 returns tag name
+%% @end
+-spec secretstream_xchacha20poly1305_tag(TagValue) -> secretstream_xchacha20poly1305_tag()
+ when TagValue :: pos_integer().
+secretstream_xchacha20poly1305_tag(?CRYPTO_SECRETSTREAM_TAG_MESSAGE) ->
+ message;
+secretstream_xchacha20poly1305_tag(?CRYPTO_SECRETSTREAM_TAG_PUSH) ->
+ push;
+secretstream_xchacha20poly1305_tag(?CRYPTO_SECRETSTREAM_TAG_REKEY) ->
+ rekey;
+secretstream_xchacha20poly1305_tag(?CRYPTO_SECRETSTREAM_TAG_FINAL) ->
+ final;
+secretstream_xchacha20poly1305_tag(Other) ->
+ Other.
%% Obtaining random bytes
+%% ----------------------
%% @doc randombytes/1 produces a stream of random bytes of the given size
%%
@@ -1119,6 +1521,18 @@ aead_chacha20poly1305_MESSAGEBYTES_MAX() ->
randombytes(N) ->
enacl_nif:randombytes(N).
+%% @doc randombytes_uint32/0 produces an integer in the 32bit range
+%% @end
+-spec randombytes_uint32() -> integer().
+randombytes_uint32() ->
+ enacl_nif:randombytes_uint32().
+
+%% @doc randombytes_uniform/1 produces a random integer in the space [0..N)
+%% That is with the upper bound excluded. Fails for integers above 32bit size
+%% @end
+randombytes_uniform(N) when N < ?MAX_32BIT_INT ->
+ enacl_nif:randombytes_uniform(N).
+
%% Helpers
%% @doc bump/4 bumps a reduction budget linearly before returning the result
diff --git a/src/enacl_nif.erl b/src/enacl_nif.erl
index 798ef53..3ecb962 100644
--- a/src/enacl_nif.erl
+++ b/src/enacl_nif.erl
@@ -25,8 +25,10 @@
crypto_sign_PUBLICKEYBYTES/0,
crypto_sign_SECRETKEYBYTES/0,
+ crypto_sign_SEEDBYTES/0,
crypto_sign_keypair/0,
+ crypto_sign_seed_keypair/1,
crypto_sign/2,
crypto_sign_open/2,
@@ -34,6 +36,11 @@
crypto_sign_detached/2,
crypto_sign_verify_detached/3,
+ crypto_sign_init/0,
+ crypto_sign_update/2,
+ crypto_sign_final_create/2,
+ crypto_sign_final_verify/3,
+
crypto_box_seal/2,
crypto_box_seal_open/3,
crypto_box_SEALBYTES/0
@@ -67,12 +74,19 @@
crypto_stream_xor/3,
crypto_stream_xor_b/3,
- crypto_aead_chacha20poly1305_encrypt/4,
- crypto_aead_chacha20poly1305_decrypt/4,
- crypto_aead_chacha20poly1305_KEYBYTES/0,
- crypto_aead_chacha20poly1305_NPUBBYTES/0,
- crypto_aead_chacha20poly1305_ABYTES/0,
- crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX/0,
+ crypto_aead_chacha20poly1305_ietf_encrypt/4,
+ crypto_aead_chacha20poly1305_ietf_decrypt/4,
+ crypto_aead_chacha20poly1305_ietf_KEYBYTES/0,
+ crypto_aead_chacha20poly1305_ietf_NPUBBYTES/0,
+ crypto_aead_chacha20poly1305_ietf_ABYTES/0,
+ crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX/0,
+
+ crypto_aead_xchacha20poly1305_ietf_encrypt/4,
+ crypto_aead_xchacha20poly1305_ietf_decrypt/4,
+ crypto_aead_xchacha20poly1305_ietf_KEYBYTES/0,
+ crypto_aead_xchacha20poly1305_ietf_NPUBBYTES/0,
+ crypto_aead_xchacha20poly1305_ietf_ABYTES/0,
+ crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX/0,
crypto_auth_BYTES/0,
crypto_auth_KEYBYTES/0,
@@ -105,6 +119,7 @@
%% Ed 25519
-export([
crypto_sign_ed25519_keypair/0,
+ crypto_sign_ed25519_sk_to_pk/1,
crypto_sign_ed25519_public_to_curve25519/1,
crypto_sign_ed25519_secret_to_curve25519/1,
crypto_sign_ed25519_PUBLICKEYBYTES/0,
@@ -132,11 +147,19 @@
%% Password Hashing - Argon2 Algorithm
-export([
- crypto_pwhash/2,
- crypto_pwhash_str/1,
+ crypto_pwhash_SALTBYTES/0,
+ crypto_pwhash/5,
+ crypto_pwhash_str/3,
crypto_pwhash_str_verify/2
]).
+%% Key Derivation
+-export([
+ crypto_kdf_KEYBYTES/0,
+ crypto_kdf_CONTEXTBYTES/0,
+ crypto_kdf_derive_from_key/3
+ ]).
+
%% Generic hash
-export([
crypto_generichash_BYTES/0,
@@ -147,13 +170,33 @@
crypto_generichash_KEYBYTES_MAX/0,
crypto_generichash/3,
crypto_generichash_init/2,
- crypto_generichash_update/3,
- crypto_generichash_final/2
+ crypto_generichash_update/2,
+ crypto_generichash_final/1
+ ]).
+
+%% Secretstream
+-export([
+ crypto_secretstream_xchacha20poly1305_ABYTES/0,
+ crypto_secretstream_xchacha20poly1305_HEADERBYTES/0,
+ crypto_secretstream_xchacha20poly1305_KEYBYTES/0,
+ crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX/0,
+ crypto_secretstream_xchacha20poly1305_TAG_MESSAGE/0,
+ crypto_secretstream_xchacha20poly1305_TAG_PUSH/0,
+ crypto_secretstream_xchacha20poly1305_TAG_REKEY/0,
+ crypto_secretstream_xchacha20poly1305_TAG_FINAL/0,
+ crypto_secretstream_xchacha20poly1305_keygen/0,
+ crypto_secretstream_xchacha20poly1305_init_push/1,
+ crypto_secretstream_xchacha20poly1305_push/4,
+ crypto_secretstream_xchacha20poly1305_init_pull/2,
+ crypto_secretstream_xchacha20poly1305_pull/3,
+ crypto_secretstream_xchacha20poly1305_rekey/1
]).
%% Access to the RNG
-export([
- randombytes/1
+ randombytes/1,
+ randombytes_uint32/0,
+ randombytes_uniform/1
]).
%% Undocumented features :>
@@ -185,13 +228,33 @@ crypto_generichash_KEYBYTES_MAX() -> erlang:nif_error(nif_not_loaded).
crypto_generichash(_HashSize, _Message, _Key) -> erlang:nif_error(nif_not_loaded).
crypto_generichash_init(_HashSize, _Key) -> erlang:nif_error(nif_not_loaded).
-crypto_generichash_update(_HashSize, _HashState, _Message) -> erlang:nif_error(nif_not_loaded).
-crypto_generichash_final(_HashSize, _HashState) -> erlang:nif_error(nif_not_loaded).
+crypto_generichash_update(_HashState, _Message) -> erlang:nif_error(nif_not_loaded).
+crypto_generichash_final(_HashState) -> erlang:nif_error(nif_not_loaded).
-crypto_pwhash(_Password, _Salt) -> erlang:nif_error(nif_not_loaded).
-crypto_pwhash_str(_Password) -> erlang:nif_error(nif_not_loaded).
+crypto_secretstream_xchacha20poly1305_ABYTES() -> erlang:nif_error(nif_not_loaded).
+crypto_secretstream_xchacha20poly1305_HEADERBYTES() -> erlang:nif_error(nif_not_loaded).
+crypto_secretstream_xchacha20poly1305_KEYBYTES() -> erlang:nif_error(nif_not_loaded).
+crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX() -> erlang:nif_error(nif_not_loaded).
+crypto_secretstream_xchacha20poly1305_TAG_MESSAGE() -> erlang:nif_error(nif_not_loaded).
+crypto_secretstream_xchacha20poly1305_TAG_PUSH() -> erlang:nif_error(nif_not_loaded).
+crypto_secretstream_xchacha20poly1305_TAG_REKEY() -> erlang:nif_error(nif_not_loaded).
+crypto_secretstream_xchacha20poly1305_TAG_FINAL() -> erlang:nif_error(nif_not_loaded).
+crypto_secretstream_xchacha20poly1305_keygen() -> erlang:nif_error(nif_not_loaded).
+crypto_secretstream_xchacha20poly1305_init_push(_Key) -> erlang:nif_error(nif_not_loaded).
+crypto_secretstream_xchacha20poly1305_push(_Ref, _Message, _AD, _Tag) -> erlang:nif_error(nif_not_loaded).
+crypto_secretstream_xchacha20poly1305_init_pull(_Header, _Key) -> erlang:nif_error(nif_not_loaded).
+crypto_secretstream_xchacha20poly1305_pull(_Ref, _CipherText, _AD) -> erlang:nif_error(nif_not_loaded).
+crypto_secretstream_xchacha20poly1305_rekey(_Ref) -> erlang:nif_error(nif_not_loaded).
+
+crypto_pwhash_SALTBYTES() -> erlang:nif_error(nif_not_loaded).
+crypto_pwhash(_Password, _Salt, _Ops, _Mem, _Alg) -> erlang:nif_error(nif_not_loaded).
+crypto_pwhash_str(_Password, _Ops, _Mem) -> erlang:nif_error(nif_not_loaded).
crypto_pwhash_str_verify(_HashedPassword, _Password) -> erlang:nif_error(nif_not_loaded).
+crypto_kdf_KEYBYTES() -> erlang:nif_error(nif_not_loaded).
+crypto_kdf_CONTEXTBYTES() -> erlang:nif_error(nif_not_loaded).
+crypto_kdf_derive_from_key(_MasterKey, _Context, _Id) -> erlang:nif_error(nif_not_loaded).
+
crypto_box_NONCEBYTES() -> erlang:nif_error(nif_not_loaded).
crypto_box_ZEROBYTES() -> erlang:nif_error(nif_not_loaded).
crypto_box_BOXZEROBYTES() -> erlang:nif_error(nif_not_loaded).
@@ -211,8 +274,10 @@ crypto_box_open_afternm_b(_CipherText, _Nonce, _K) -> erlang:nif_error(nif_not_l
crypto_sign_PUBLICKEYBYTES() -> erlang:nif_error(nif_not_loaded).
crypto_sign_SECRETKEYBYTES() -> erlang:nif_error(nif_not_loaded).
+crypto_sign_SEEDBYTES() -> erlang:nif_error(nif_not_loaded).
crypto_sign_keypair() -> erlang:nif_error(nif_not_loaded).
+crypto_sign_seed_keypair(_S) -> erlang:nif_error(nif_not_loaded).
crypto_sign(_M, _SK) -> erlang:nif_error(nif_not_loaded).
crypto_sign_open(_SignedMessage, _PK) -> erlang:nif_error(nif_not_loaded).
@@ -220,6 +285,11 @@ crypto_sign_detached(_M, _SK) -> erlang:nif_error(nif_not_loaded).
crypto_sign_verify_detached(_Sig, _M, _PK) -> erlang:nif_error(nif_not_loaded).
+crypto_sign_init() -> erlang:nif_error(nif_not_loaded).
+crypto_sign_update(_S, _M) -> erlang:nif_error(nif_not_loaded).
+crypto_sign_final_create(_S, _SK) -> erlang:nif_error(nif_not_loaded).
+crypto_sign_final_verify(_State, _Sig, _PK) -> erlang:nif_error(nif_not_loaded).
+
crypto_box_seal(_Msg, _PK) -> erlang:nif_error(nif_not_loaded).
crypto_box_seal_open(_CipherText, _PK, _SK) -> erlang:nif_error(nif_not_loaded).
crypto_box_SEALBYTES() -> erlang:nif_error(nif_not_loaded).
@@ -248,12 +318,19 @@ crypto_stream_b(_Bytes, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
crypto_stream_xor(_M, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
crypto_stream_xor_b(_M, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
-crypto_aead_chacha20poly1305_encrypt(_Key, _Nonce, _AD, _Message) -> erlang:nif_error(nif_not_loaded).
-crypto_aead_chacha20poly1305_decrypt(_Key, _Nonce, _AD, _Message) -> erlang:nif_error(nif_not_loaded).
-crypto_aead_chacha20poly1305_KEYBYTES() -> erlang:nif_error(nif_not_loaded).
-crypto_aead_chacha20poly1305_NPUBBYTES() -> erlang:nif_error(nif_not_loaded).
-crypto_aead_chacha20poly1305_ABYTES() -> erlang:nif_error(nif_not_loaded).
-crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX() -> erlang:nif_error(nif_not_loaded).
+crypto_aead_chacha20poly1305_ietf_encrypt(_Message, _AD, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
+crypto_aead_chacha20poly1305_ietf_decrypt(_CipherText, _AD, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
+crypto_aead_chacha20poly1305_ietf_KEYBYTES() -> erlang:nif_error(nif_not_loaded).
+crypto_aead_chacha20poly1305_ietf_NPUBBYTES() -> erlang:nif_error(nif_not_loaded).
+crypto_aead_chacha20poly1305_ietf_ABYTES() -> erlang:nif_error(nif_not_loaded).
+crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX() -> erlang:nif_error(nif_not_loaded).
+
+crypto_aead_xchacha20poly1305_ietf_encrypt(_Message, _AD, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
+crypto_aead_xchacha20poly1305_ietf_decrypt(_CipherText, _AD, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
+crypto_aead_xchacha20poly1305_ietf_KEYBYTES() -> erlang:nif_error(nif_not_loaded).
+crypto_aead_xchacha20poly1305_ietf_NPUBBYTES() -> erlang:nif_error(nif_not_loaded).
+crypto_aead_xchacha20poly1305_ietf_ABYTES() -> erlang:nif_error(nif_not_loaded).
+crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX() -> erlang:nif_error(nif_not_loaded).
crypto_auth_BYTES() -> erlang:nif_error(nif_not_loaded).
crypto_auth_KEYBYTES() -> erlang:nif_error(nif_not_loaded).
@@ -277,6 +354,7 @@ crypto_curve25519_scalarmult(_Secret, _BasePoint) -> erlang:nif_error(nif_not_lo
crypto_curve25519_scalarmult_base(_Secret) -> erlang:nif_error(nif_not_loaded).
crypto_sign_ed25519_keypair() -> erlang:nif_error(nif_not_loaded).
+crypto_sign_ed25519_sk_to_pk(_SecretKey) -> erlang:nif_error(nif_not_loaded).
crypto_sign_ed25519_public_to_curve25519(_PublicKey) -> erlang:nif_error(nif_not_loaded).
crypto_sign_ed25519_secret_to_curve25519(_SecretKey) -> erlang:nif_error(nif_not_loaded).
crypto_sign_ed25519_PUBLICKEYBYTES() -> erlang:nif_error(nif_not_loaded).
@@ -296,5 +374,7 @@ crypto_kx_PUBLICKEYBYTES() -> erlang:nif_error(nif_not_loaded).
crypto_kx_SECRETKEYBYTES() -> erlang:nif_error(nif_not_loaded).
randombytes(_RequestedSize) -> erlang:nif_error(nif_not_loaded).
+randombytes_uint32() -> erlang:nif_error(nif_not_loaded).
+randombytes_uniform(_UpperBound) -> erlang:nif_error(nif_not_loaded).
scramble_block_16(_Block, _Key) -> erlang:nif_error(nif_not_loaded).
diff --git a/test/enacl_SUITE.erl b/test/enacl_SUITE.erl
new file mode 100644
index 0000000..cd8f229
--- /dev/null
+++ b/test/enacl_SUITE.erl
@@ -0,0 +1,196 @@
+-module(enacl_SUITE).
+-include_lib("common_test/include/ct.hrl").
+
+-compile([export_all, nowarn_export_all]).
+
+suite() ->
+ [{timetrap, {seconds, 30}}].
+
+init_per_group(_Group, Config) ->
+ Config.
+
+end_per_group(_Group, _Config) ->
+ ok.
+
+init_per_suite(Config) ->
+ application:ensure_all_started(enacl),
+ Config.
+
+end_per_suite(_Config) ->
+ application:stop(enacl),
+ ok.
+
+init_per_testcase(x, Config) ->
+ {ok, _} = dbg:tracer(),
+ dbg:p(all, c),
+ dbg:tpl(graphql_execute, lookup_field, '_', cx),
+ Config;
+init_per_testcase(_Case, Config) ->
+ Config.
+
+end_per_testcase(x, _Config) ->
+ dbg:stop_clear(),
+ ok;
+end_per_testcase(_Case, _Config) ->
+ ok.
+
+groups() ->
+ Neg = {negative, [shuffle, parallel],
+ [generichash_basic_neg]},
+ Pos = {positive, [shuffle, parallel],
+ [
+ aead_chacha20poly1305_ietf,
+ aead_xchacha20poly1305,
+ generichash_basic_pos,
+ generichash_chunked,
+ kx,
+ pwhash,
+ secretstream,
+ sign,
+ verify_detached
+ ]},
+
+ [Neg, Pos].
+
+all() ->
+ [{group, negative},
+ {group, positive}].
+
+%% -- BASIC --------------------------------------
+generichash_basic_neg(_Config) ->
+ %% Negative generichash invocations
+ Msg = <<"I've seen things you people wouldn't believe: attack ships on fire off the shoulder of Orion. "
+ "I've watched C-beams glitter in the dark near the Tannhäuser Gate. "
+ "All those... moments... will be lost... in time, like... tears... in rain">>,
+ Key = <<"Hash Key 123456789">>,
+ {'EXIT', {badarg, _}} = (catch enacl:generichash(9, Msg, Key)),
+ {'EXIT', {badarg, _}} = (catch enacl:generichash(65, Msg, Key)),
+ {'EXIT', {badarg, _}} = (catch enacl:generichash(32, Msg, <<"Small">>)),
+ ok.
+
+generichash_basic_pos(_Config) ->
+ Msg = <<"I've seen things you people wouldn't believe: attack ships on fire off the shoulder of Orion. "
+ "I've watched C-beams glitter in the dark near the Tannhäuser Gate. "
+ "All those... moments... will be lost... in time, like... tears... in rain">>,
+ Key = <<"Hash Key 123456789">>,
+ <<189,104,45,187,170,229,212,4,121,43,137,74,241,173,181,77,
+ 67,211,133,70,196,6,128,97>> = enacl:generichash(24, Msg, Key),
+ ok.
+
+generichash_chunked(_Config) ->
+ Msg = <<"I've seen things you people wouldn't believe: attack ships on fire off the shoulder of Orion. "
+ "I've watched C-beams glitter in the dark near the Tannhäuser Gate. "
+ "All those... moments... will be lost... in time, like... tears... in rain">>,
+ Key = <<"Hash Key 123456789">>,
+ State = enacl:generichash_init(24, Key),
+ State = generichash_chunked(State, Msg, 10000),
+ Expected = <<46,49,32,18,13,186,182,105,106,122,253,139,89,176,169,141,
+ 73,93,99,6,41,216,110,41>>,
+ Expected = enacl:generichash_final(State),
+ try enacl:generichash_final(State) of
+ _ -> ct:fail(must_finalize)
+ catch
+ error:enacl_finalized ->
+ ok
+ end,
+ try enacl:generichash_update(State, <<"x">>) of
+ _ -> ct:fail(must_finalize)
+ catch
+ error:enacl_finalized ->
+ ok
+ end,
+ ok.
+
+generichash_chunked(State, _Msg, 0) -> State;
+generichash_chunked(State, Msg, N) ->
+ State2 = enacl:generichash_update(State, Msg),
+ generichash_chunked(State2, Msg, N-1).
+
+aead_xchacha20poly1305(_Config) ->
+ NonceLen = enacl:aead_xchacha20poly1305_ietf_NPUBBYTES(),
+ KLen = enacl:aead_xchacha20poly1305_ietf_KEYBYTES(),
+ Key = binary:copy(<<"K">>, KLen),
+ Msg = <<"test">>,
+ AD = <<1,2,3,4,5,6>>,
+ Nonce = binary:copy(<<"N">>, NonceLen),
+
+ CipherText = enacl:aead_xchacha20poly1305_ietf_encrypt(Msg, AD, Nonce, Key),
+ Msg = enacl:aead_xchacha20poly1305_ietf_decrypt(CipherText, AD, Nonce, Key),
+ ok.
+
+aead_chacha20poly1305_ietf(_Config) ->
+ NonceLen = enacl:aead_chacha20poly1305_ietf_NPUBBYTES(),
+ KLen = enacl:aead_chacha20poly1305_ietf_KEYBYTES(),
+ Key = binary:copy(<<"K">>, KLen),
+ Msg = <<"test">>,
+ AD = <<1,2,3,4,5,6>>,
+ Nonce = binary:copy(<<"N">>, NonceLen),
+
+ CipherText = enacl:aead_chacha20poly1305_ietf_encrypt(Msg, AD, Nonce, Key),
+ Msg = enacl:aead_chacha20poly1305_ietf_decrypt(CipherText, AD, Nonce, Key),
+ ok.
+
+pwhash(_Config) ->
+ PW = <<"XYZZY">>,
+ Salt = <<"1234567890abcdef">>,
+ Hash1 = <<164,75,127,151,168,101,55,77,48,77,240,204,64,20,43,23,88,
+ 18,133,11,53,151,2,113,232,95,84,165,50,7,60,20>>,
+ Hash1 = enacl:pwhash(PW, Salt),
+ Str1 = enacl:pwhash_str(PW),
+ true = enacl:pwhash_str_verify(Str1, PW),
+ false = enacl:pwhash_str_verify(Str1, <>),
+ 16 = enacl:pwhash_SALTBYTES(),
+ ok.
+
+sign(_Config) ->
+ #{public := PK, secret := SK} = enacl:sign_keypair(),
+ Msg = <<"Test">>,
+ State = enacl:sign_init(),
+ Create = sign_chunked(State, Msg, 10000),
+ {ok, Signature} = enacl:sign_final_create(Create, SK),
+ StateVerify = enacl:sign_init(),
+ Verify = sign_chunked(StateVerify, Msg, 10000),
+ true = enacl:sign_final_verify(Verify, Signature, PK),
+ ok.
+
+sign_chunked(S, _M, 0) -> S;
+sign_chunked(S, M, N) ->
+ S2 = enacl:sign_update(S, M),
+ sign_chunked(S2, M, N-1).
+
+kx(_Config) ->
+ #{ public := CPK, secret := CSK} = enacl:kx_keypair(),
+ #{ public := SPK, secret := SSK} = enacl:kx_keypair(),
+ #{ client_tx := CTX, client_rx := CRX} = enacl:kx_client_session_keys(CPK, CSK, SPK),
+ #{ server_tx := STX, server_rx := SRX} = enacl:kx_server_session_keys(SPK, SSK, CPK),
+ %% Verify we got a shared keypair
+ CTX = SRX,
+ STX = CRX,
+ ok.
+
+secretstream(_Config) ->
+ Part1 = <<"Arbitrary data to encrypt">>,
+ Part2 = <<"split into">>,
+ Part3 = <<"three messages">>,
+
+ Key = enacl:secretstream_xchacha20poly1305_keygen(),
+
+ %% Encrypt
+ {Header, State} = enacl:secretstream_xchacha20poly1305_init_push(Key),
+ Block1 = enacl:secretstream_xchacha20poly1305_push(State, Part1, <<"AD1">>, message),
+ Block2 = enacl:secretstream_xchacha20poly1305_push(State, Part2, <<>>, message),
+ Block3 = enacl:secretstream_xchacha20poly1305_push(State, Part3, <<"AD3">>, final),
+
+ %% Decrypt
+ DState = enacl:secretstream_xchacha20poly1305_init_pull(Header, Key),
+ {Part1, message} = enacl:secretstream_xchacha20poly1305_pull(DState, Block1, <<"AD1">>),
+ {Part2, message} = enacl:secretstream_xchacha20poly1305_pull(DState, Block2, <<>>),
+ {Part3, final} = enacl:secretstream_xchacha20poly1305_pull(DState, Block3, <<"AD3">>),
+ ok.
+
+verify_detached(_Config) ->
+ #{ public := PK, secret := SK} = enacl:sign_keypair(),
+ M = <<"Arbitrary data to encrypt">>,
+ Sig = enacl:sign_detached(M, SK),
+ true = enacl:sign_verify_detached(Sig, M, PK),
+ ok.