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.