Compare commits
236 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4eb7ec7008 | ||
![]() |
a3d010ba62 | ||
![]() |
fa94eaf6f6 | ||
![]() |
67fceef42c | ||
![]() |
80e24670d9 | ||
![]() |
793ddb502f | ||
![]() |
db9338e3d4 | ||
![]() |
46e2754a50 | ||
![]() |
ddc5d1269d | ||
![]() |
b229e9054b | ||
![]() |
c0afa5890d | ||
![]() |
20b230d693 | ||
![]() |
47ff11e956 | ||
![]() |
ec64b0de73 | ||
![]() |
87f9270a6c | ||
![]() |
756c222877 | ||
![]() |
a271797641 | ||
![]() |
458c4ca9bc | ||
![]() |
6242282bfd | ||
![]() |
d801018f23 | ||
![]() |
bb1334d82f | ||
![]() |
9331ef974e | ||
![]() |
129f5e2acf | ||
![]() |
36cab6aedb | ||
![]() |
b81fa6f0d6 | ||
![]() |
e748552809 | ||
![]() |
f301929d75 | ||
![]() |
529d8f6033 | ||
![]() |
8b8fe9fa98 | ||
![]() |
8ac26c8161 | ||
![]() |
f8bd7f3565 | ||
![]() |
61b83710b5 | ||
![]() |
6cc2529023 | ||
![]() |
fe33927fb3 | ||
![]() |
b7034392bd | ||
![]() |
c6f4e21c9a | ||
![]() |
e41634b75c | ||
![]() |
f879ec00a7 | ||
![]() |
ba69139283 | ||
![]() |
ef049c921f | ||
![]() |
aca072630e | ||
![]() |
5f22892ce4 | ||
![]() |
e59656ebf6 | ||
![]() |
f99c8643f0 | ||
![]() |
3a4d000c56 | ||
![]() |
73a6462ef3 | ||
![]() |
0b8abb95d5 | ||
![]() |
3d9cc841eb | ||
![]() |
ea72835b50 | ||
![]() |
d6bd999c82 | ||
![]() |
0855ce2f55 | ||
![]() |
7f06675533 | ||
![]() |
87282dc4f8 | ||
![]() |
1bb0d59c7f | ||
![]() |
442094df0d | ||
![]() |
a45b433f88 | ||
![]() |
868a14c25d | ||
![]() |
9919871e53 | ||
![]() |
9bdb1ccf1b | ||
![]() |
57ad262ec8 | ||
![]() |
44f2220532 | ||
![]() |
6ca6b0b22e | ||
![]() |
d67e047859 | ||
![]() |
cbb06fc598 | ||
![]() |
0351de9882 | ||
![]() |
e18f9b7337 | ||
![]() |
b2c70ef6a3 | ||
![]() |
025b83a14b | ||
![]() |
4b4ec373b1 | ||
![]() |
7f7c667b28 | ||
![]() |
90be3aa2dd | ||
![]() |
23bd40a4b3 | ||
![]() |
42fd03cde0 | ||
![]() |
9258d8b6ad | ||
![]() |
5ea33643da | ||
![]() |
a45d3863e0 | ||
![]() |
82cd5d0361 | ||
![]() |
4775270d76 | ||
![]() |
00f895b488 | ||
![]() |
a001404877 | ||
![]() |
812f05adcf | ||
![]() |
5f95ee314f | ||
![]() |
bde03dc557 | ||
![]() |
061c3dfd65 | ||
![]() |
ec3af40ef8 | ||
![]() |
8361450745 | ||
![]() |
67fe9c7863 | ||
![]() |
55b3670fe7 | ||
![]() |
e32c923822 | ||
![]() |
220ac6640a | ||
![]() |
218a6db09c | ||
![]() |
2045ca8e09 | ||
![]() |
8c13fc682e | ||
![]() |
24859776e4 | ||
![]() |
1f7d553f75 | ||
![]() |
8e628a61fc | ||
![]() |
a3f112607c | ||
![]() |
96b883ceba | ||
![]() |
d013a04a58 | ||
![]() |
fd87b9da39 | ||
![]() |
cceef4530a | ||
![]() |
014d50cf47 | ||
![]() |
f580f6525b | ||
![]() |
7999d08e9d | ||
![]() |
1cb2c3a2a2 | ||
![]() |
dea9ce62ed | ||
![]() |
41045fed85 | ||
![]() |
5d245797d2 | ||
![]() |
c26aeee413 | ||
![]() |
78621356ec | ||
![]() |
d06fff489d | ||
![]() |
c7720e6ab8 | ||
![]() |
b637ba307b | ||
![]() |
59b94439d1 | ||
![]() |
aa2c69529a | ||
![]() |
71832cce4c | ||
![]() |
c791f602e9 | ||
![]() |
2041cec2e8 | ||
![]() |
8ed8663dfe | ||
![]() |
fc943a19c7 | ||
![]() |
2b8b6224d8 | ||
![]() |
4939f7bb23 | ||
![]() |
7d8fdf69c0 | ||
![]() |
e4b35a7035 | ||
![]() |
49a437347e | ||
![]() |
018bf0f6fc | ||
![]() |
e67619a403 | ||
![]() |
7f857115bb | ||
![]() |
bdb4719f6d | ||
![]() |
ab40d5bbf8 | ||
![]() |
92b91bd806 | ||
![]() |
fec24995d1 | ||
![]() |
899fbeefd3 | ||
![]() |
d850b16d1b | ||
![]() |
a5e66ff204 | ||
![]() |
bf6fb6cf7b | ||
![]() |
fe478ea253 | ||
![]() |
460c5bddfd | ||
![]() |
d3c033c4e6 | ||
![]() |
fddbefeabd | ||
![]() |
2ea36a7352 | ||
![]() |
0427fa42ee | ||
![]() |
f5b8a8eb3b | ||
![]() |
4eaef57a76 | ||
![]() |
3ee5a94caf | ||
![]() |
0047af286f | ||
![]() |
61345d0b6e | ||
![]() |
0f39bae64c | ||
![]() |
d7e83dd569 | ||
![]() |
df1b134f73 | ||
![]() |
f5918c0156 | ||
![]() |
463cae05d5 | ||
![]() |
6e057cbd48 | ||
![]() |
d5bb24e671 | ||
![]() |
a25b9a2684 | ||
![]() |
b7533d3b9c | ||
![]() |
f43a730758 | ||
![]() |
0cfa88be32 | ||
![]() |
3f284be0cb | ||
![]() |
03bf3b30a1 | ||
![]() |
6032b5839d | ||
![]() |
e6cb85fb24 | ||
![]() |
288d51ace7 | ||
![]() |
25d411aa7d | ||
![]() |
7f8e0a0e07 | ||
![]() |
aaa5827613 | ||
![]() |
bb703d0c35 | ||
![]() |
600020620b | ||
![]() |
97ee4bbdcf | ||
![]() |
3c8d54d87b | ||
![]() |
f9d6034e84 | ||
![]() |
bc1af327e5 | ||
![]() |
6a30dc2825 | ||
![]() |
e66855c029 | ||
![]() |
745ff9ff75 | ||
![]() |
885bc16374 | ||
![]() |
279c2c32c8 | ||
![]() |
6f4a0c2521 | ||
![]() |
26180f42c0 | ||
![]() |
2b183e1974 | ||
![]() |
3442655c5b | ||
![]() |
885662c069 | ||
![]() |
1121321573 | ||
![]() |
4afa6fc093 | ||
![]() |
26f4a40eb4 | ||
![]() |
07bcd87294 | ||
![]() |
d779071285 | ||
![]() |
b3bbb2a910 | ||
![]() |
40fde1807b | ||
![]() |
04b8fa3ecb | ||
![]() |
f650c72b02 | ||
![]() |
5210099e53 | ||
![]() |
bb4014286f | ||
![]() |
edd95498d1 | ||
![]() |
e77aca8ecb | ||
![]() |
9c25038034 | ||
![]() |
d737552b2a | ||
![]() |
2f1e1fa7b6 | ||
![]() |
3b9bc848e9 | ||
![]() |
75042a24ba | ||
![]() |
b9b6f7db11 | ||
![]() |
8b8ceff4ef | ||
![]() |
fb7de2cbb4 | ||
![]() |
0c5acd2fba | ||
![]() |
3407433443 | ||
![]() |
405045bf5c | ||
![]() |
2f50ba6289 | ||
![]() |
7181600cb4 | ||
![]() |
9dfbe8cc90 | ||
![]() |
a5dab7acf0 | ||
![]() |
fb5985da27 | ||
![]() |
b2efb45126 | ||
![]() |
d4a2a14bc9 | ||
![]() |
cd183efd34 | ||
![]() |
97ad7a5452 | ||
![]() |
c8403ab198 | ||
![]() |
dd793a86b4 | ||
![]() |
9d2f90a5c5 | ||
![]() |
c6de481b2e | ||
![]() |
5a48c66b07 | ||
![]() |
bd80d1d79d | ||
![]() |
43835abafe | ||
![]() |
cfd654c275 | ||
![]() |
fd796440db | ||
![]() |
207ec85f8c | ||
![]() |
05420f8a6b | ||
![]() |
b9f2895b4d | ||
![]() |
0e7ee5c6f0 | ||
![]() |
bf4d61680c | ||
![]() |
e524c2d5c8 | ||
![]() |
ba640b0659 | ||
![]() |
36eedc6751 | ||
![]() |
c938d3c6e8 | ||
![]() |
ee0d800a9a | ||
![]() |
43cae7c7ea | ||
![]() |
f395f65389 |
3
.github/workflows/Makefile
vendored
Normal file
3
.github/workflows/Makefile
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
all:
|
||||
cue export --out yaml > ci.yaml
|
||||
|
37
.github/workflows/actions.cue
vendored
Normal file
37
.github/workflows/actions.cue
vendored
Normal file
@ -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]
|
||||
}
|
34
.github/workflows/ci.yaml
vendored
Normal file
34
.github/workflows/ci.yaml
vendored
Normal file
@ -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:
|
||||
- "24.3"
|
||||
- "25.3"
|
||||
- "26.2"
|
||||
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
|
21
.github/workflows/setup.cue
vendored
Normal file
21
.github/workflows/setup.cue
vendored
Normal file
@ -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"]
|
||||
}
|
||||
}
|
40
.github/workflows/steps.cue
vendored
Normal file
40
.github/workflows/steps.cue
vendored
Normal file
@ -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"
|
||||
}]
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
.rebar
|
||||
.rebar3
|
||||
.envrc
|
||||
ebin
|
||||
*.beam
|
||||
*.o
|
||||
@ -12,3 +13,8 @@ doc/*.png
|
||||
doc/*.css
|
||||
_build
|
||||
/.eqc-info
|
||||
priv/enacl_nif.dll
|
||||
priv/enacl_nif.exp
|
||||
priv/enacl_nif.lib
|
||||
c_src/*.d
|
||||
|
||||
|
16
.vscode/c_cpp_properties.json
vendored
Normal file
16
.vscode/c_cpp_properties.json
vendored
Normal file
@ -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
|
||||
}
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"C_Cpp.errorSquiggles": "Disabled"
|
||||
}
|
322
CHANGELOG.md
Normal file
322
CHANGELOG.md
Normal file
@ -0,0 +1,322 @@
|
||||
# Changelog
|
||||
|
||||
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).
|
||||
|
||||
## [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 [0.17.2]
|
||||
|
||||
- Work around `rebar3 hex` publishing .so files
|
||||
|
||||
## [0.17.1]
|
||||
|
||||
### 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 [0.17.0]
|
||||
|
||||
- Expose the AEAD ChaCha20 Poly1305 (IETF) functionality (Hans
|
||||
Svensson / Quviq).
|
||||
- Expose Curve25519 Scalar Multiplication over a base point in the
|
||||
curve (Hans Svensson / Quviq)
|
||||
- Support the pwhash_* primitives (relying on Argon2) for password
|
||||
hashing (daveed-al / Venkatakumar Srinivasan)
|
||||
- Support for EQC Mini runs (Irina Guberman). The generator doesn't
|
||||
inject faults, but it does verify the positive path. This is useful
|
||||
to verify the enacl library on embedded platforms and so on.
|
||||
- Support generichash functions (Venkatakumar Srinivasan / Xaptum)
|
||||
|
||||
### Fixed [0.17.0]
|
||||
|
||||
- The type specification of generichash/2 and generichash/3 was
|
||||
corrected (Technion)
|
||||
|
||||
### 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
|
||||
dirty scheduler interface is on and enabled by default (YAZ!)
|
||||
- Some `size_t` entries in the C layer are now `uint` (Zane Beckwith).
|
||||
The change only affects messages of exorbitant sizes, which we think
|
||||
should be guarded against anyway, and it fixes some obvious
|
||||
compilation problems on 32 bit architectures, and to boot matches
|
||||
better against the Erlang NIF interface. We might change this later,
|
||||
but hopefully this is a change for the better.
|
||||
|
||||
## [0.16.0]
|
||||
|
||||
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 [0.16.0]
|
||||
|
||||
- Add kx_* functions (Alexander Malaev)
|
||||
- chacha stream functions added, siphash-2-4 added, unsafe_memzero/1
|
||||
added (no attribution)
|
||||
|
||||
### Fixed [0.16.0]
|
||||
|
||||
- Do not use the dirty-scheduler test macro as it is gone.
|
||||
|
||||
## [0.15.0]
|
||||
|
||||
### 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 [0.15.0]
|
||||
|
||||
- Many dirty-scheduler tunings have been performed to make sure we
|
||||
won't block a scheduler ever.
|
||||
- New benchmarks: `bench/timing.erl` together with DTrace scripts
|
||||
`bench/*.d`
|
||||
- Interface simplification toward the NIF api. Only execute
|
||||
instructions directly on the scheduler if the operation *really*
|
||||
benefits from doing so.
|
||||
|
||||
No functional change, but the above characteristic change may mean the
|
||||
library now behaves differently from what it did before. It should be
|
||||
a better citizen to other libraries and other parts of the system.
|
||||
|
||||
## [0.14.0]
|
||||
|
||||
### 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 [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 [0.13.0]
|
||||
|
||||
- Quell warnings from the C code
|
||||
|
||||
### 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 [0.12.1]
|
||||
|
||||
- Provide the `priv` directory for being able to properly build
|
||||
without manual intervention.
|
||||
|
||||
## [0.12.0]
|
||||
|
||||
### 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).
|
||||
- Introduce Curve25519 manipulations into the extension interface.
|
||||
- Write (rudimentary) QuickCheck tests for the new interface, to
|
||||
verify its correctness.
|
||||
|
||||
## [0.11.0]
|
||||
|
||||
### 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 [0.11.0]
|
||||
|
||||
- Fix type for `enacl:box_open/4`. The specification was wrong which
|
||||
results in errors in other applications using enacl.
|
||||
|
||||
## [0.10.2]
|
||||
|
||||
Maintenance release. Fix some usability problems with the library.
|
||||
|
||||
### 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)
|
||||
- Fix a wrong call in the timing code. Luckily, this error has not
|
||||
affected anything as it has only replaced a verification call with
|
||||
one that does not verify. In practice, the timing is roughly the
|
||||
same for both, save for a small constant factor (Thanks to the
|
||||
dialyzer)
|
||||
- Improve documentation around installation/building the software.
|
||||
Hopefully it is now more prominent (Thanks to David N. Welton)
|
||||
|
||||
## [0.10.1]
|
||||
|
||||
### 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
|
||||
`largebinary`-support in EQC tests.
|
||||
- The release also adds an (experimental) scrambling function for
|
||||
hiding the internal structure of counters. This is based on an
|
||||
enlarged TEA-cipher by Wheeler and Needham. It is neccessary for
|
||||
correct operation of the CurveCP implementation, which is why it is
|
||||
included in this library.
|
||||
|
||||
## [0.10.0]
|
||||
|
||||
Ultra-late beta; tuning for the last couple of functions which could
|
||||
be nice to have.
|
||||
|
||||
### 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`
|
||||
|
||||
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
|
||||
gives no added security benefit. Key generation in NaCl relies on
|
||||
`/dev/urandom`. Go relies on `/dev/urandom`. It is about time Erlang
|
||||
does as well.
|
||||
|
||||
## [0.9.0]
|
||||
|
||||
Ultra-late beta. Code probably works, but it requires some real-world
|
||||
use before it is deemed entirely stable.
|
||||
|
||||
Initial release.
|
@ -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
|
||||
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Jesper Louis Andersen
|
||||
Copyright (c) 2014-2018 Jesper Louis Andersen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
23
Makefile
23
Makefile
@ -1,8 +1,29 @@
|
||||
REBAR=rebar3
|
||||
RUN_EQC=erl -pa _build/default/lib/enacl/ebin -noshell -s enacl_eqc -s init stop
|
||||
|
||||
.PHONY: compile
|
||||
compile:
|
||||
$(REBAR) compile | sed -e 's|_build/default/lib/enacl/||g'
|
||||
$(REBAR) compile
|
||||
|
||||
.PHONY: tests
|
||||
tests:
|
||||
$(REBAR) ct
|
||||
|
||||
eqc_compile: compile
|
||||
erlc -o _build/default/lib/enacl/ebin eqc_test/enacl_eqc.erl
|
||||
|
||||
eqc_mini_compile: compile
|
||||
erlc -Dmini -o _build/default/lib/enacl/ebin eqc_test/enacl_eqc.erl
|
||||
|
||||
eqc_run: eqc_compile
|
||||
$(RUN_EQC)
|
||||
|
||||
eqc_mini_run: eqc_mini_compile
|
||||
$(RUN_EQC)
|
||||
|
||||
.PHONE: console
|
||||
console: compile
|
||||
$(REBAR) shell
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
|
178
README.md
178
README.md
@ -6,17 +6,15 @@ 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.
|
||||
|
||||
* 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
|
||||
@ -30,10 +28,18 @@ or
|
||||
|
||||
rebar compile
|
||||
|
||||
## Features:
|
||||
To build and run licensed eqc test execute:
|
||||
|
||||
make eqc_run
|
||||
|
||||
To build and run eqc-mini version of test execute:
|
||||
|
||||
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,
|
||||
@ -52,16 +58,17 @@ 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 NaCl documentation at
|
||||
In general, consult the libsodium documentation at [Libsodium documentation](https://download.libsodium.org/doc/)
|
||||
|
||||
https://nacl.cr.yp.to
|
||||
The original NaCl documentation is nowadays largely superceded by the
|
||||
libsodium documentation, but it is still worth a visit [NaCl website](https://nacl.cr.yp.to)
|
||||
|
||||
but also note that our interface has full Edoc documentation,
|
||||
generated by executing
|
||||
|
||||
rebar doc
|
||||
rebar3 doc
|
||||
|
||||
## Hints
|
||||
|
||||
@ -119,135 +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
|
||||
|
||||
### v0.16.0
|
||||
See CHANGELOG.md
|
||||
|
||||
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.
|
||||
|
||||
Features:
|
||||
|
||||
* Add kx_* functions (Alexander Malaev)
|
||||
* chacha stream functions added, siphash-2-4 added, unsafe_memzero/1
|
||||
added (no attribution)
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Do not use the dirty-scheduler test macro as it is gone.
|
||||
|
||||
### v0.15.0
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* 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.
|
||||
|
||||
Features:
|
||||
|
||||
* Many dirty-scheduler tunings have been performed to make sure we
|
||||
won't block a scheduler ever.
|
||||
* New benchmarks: `bench/timing.erl` together with DTrace scripts
|
||||
`bench/*.d`
|
||||
* Interface simplification toward the NIF api. Only execute
|
||||
instructions directly on the scheduler if the operation *really*
|
||||
benefits from doing so.
|
||||
|
||||
No functional change, but the above characteristic change may mean the
|
||||
library now behaves differently from what it did before. It should be
|
||||
a better citizen to other libraries and other parts of the system.
|
||||
|
||||
### v0.14.0
|
||||
|
||||
* Add support for libsodiums `box_seal` functions (Amir Ghassemi Nasr)
|
||||
* Add support for libsodiums `crypto_sign_detached` (Joel Stanley,
|
||||
Parnell Springmeyer)
|
||||
* 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.
|
||||
|
||||
### v0.13.0
|
||||
|
||||
* Quell warnings from the C code
|
||||
* Add Ed 25519 utility API (Alexander Færøy)
|
||||
* Add FreeBSD support for the NIF compilation (Ricardo Lanziano)
|
||||
|
||||
### v0.12.1
|
||||
|
||||
* Provide the `priv` directory for being able to properly build
|
||||
without manual intervention.
|
||||
|
||||
### v0.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).
|
||||
* Introduce Curve25519 manipulations into the extension interface.
|
||||
* Write (rudimentary) QuickCheck tests for the new interface, to
|
||||
verify its correctness.
|
||||
|
||||
### v0.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.
|
||||
* Fix type for `enacl:box_open/4`. The specification was wrong which
|
||||
results in errors in other applications using enacl.
|
||||
|
||||
### v0.10.2
|
||||
|
||||
Maintenance release. Fix some usability problems with the library.
|
||||
|
||||
* 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)
|
||||
* Fix a wrong call in the timing code. Luckily, this error has not
|
||||
affected anything as it has only replaced a verification call with
|
||||
one that does not verify. In practice, the timing is roughly the
|
||||
same for both, save for a small constant factor (Thanks to the
|
||||
dialyzer)
|
||||
* Improve documentation around installation/building the software.
|
||||
Hopefully it is now more prominent (Thanks to David N. Welton)
|
||||
|
||||
### v0.10.1
|
||||
|
||||
This small patch-release provides tests for the `randombytes/1`
|
||||
function call, and optimizes EQC tests to make it easier to implement
|
||||
`largebinary`-support in EQC tests. The release also adds an
|
||||
(experimental) scrambling function for hiding the internal structure
|
||||
of counters. This is based on an enlarged TEA-cipher by Wheeler and
|
||||
Needham. It is neccessary for correct operation of the CurveCP
|
||||
implementation, which is why it is included in this library.
|
||||
|
||||
### v0.10.0
|
||||
|
||||
Ultra-late beta; tuning for the last couple of functions which could
|
||||
be nice to have. 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`
|
||||
|
||||
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
|
||||
gives no added security benefit. Key generation in NaCl relies on
|
||||
`/dev/urandom`. Go relies on `/dev/urandom`. It is about time Erlang
|
||||
does as well.
|
||||
|
||||
### v0.9.0
|
||||
|
||||
Ultra-late beta. Code probably works, but it requires some real-world
|
||||
use before it is deemed entirely stable.
|
||||
|
||||
Initial release.
|
||||
|
||||
# Overview
|
||||
## Overview
|
||||
|
||||
The NaCl cryptographic library provides a number of different
|
||||
cryptographic primitives. In the following, we split up the different
|
||||
@ -309,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
|
||||
@ -361,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
|
||||
@ -388,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.
|
||||
|
@ -47,7 +47,7 @@ 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;
|
||||
@ -62,7 +62,7 @@ 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.
|
||||
|
||||
@ -100,8 +100,8 @@ 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;
|
||||
@ -111,7 +111,7 @@ 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) ->
|
||||
@ -120,7 +120,7 @@ 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;
|
||||
@ -130,7 +130,7 @@ auth(Msg, Key, N) ->
|
||||
|
||||
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;
|
||||
|
@ -1,75 +0,0 @@
|
||||
# Based on c_src.mk from erlang.mk by Loïc Hoguin <essen@ninenines.eu>
|
||||
|
||||
PROJECT ?= enacl_nif
|
||||
|
||||
ERTS_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~s/erts-~s/include/\", [code:root_dir(), erlang:system_info(version)]).")
|
||||
ERL_INTERFACE_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~s\", [code:lib_dir(erl_interface, include)]).")
|
||||
ERL_INTERFACE_LIB_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~s\", [code:lib_dir(erl_interface, lib)]).")
|
||||
|
||||
C_SRC_DIR = $(CURDIR)
|
||||
C_SRC_OUTPUT ?= $(CURDIR)/../priv/$(PROJECT).so
|
||||
|
||||
# System type and C compiler/flags.
|
||||
|
||||
UNAME_SYS := $(shell uname -s)
|
||||
ifeq ($(UNAME_SYS), Darwin)
|
||||
CC ?= cc
|
||||
CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes
|
||||
CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall
|
||||
LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress
|
||||
else ifeq ($(UNAME_SYS), FreeBSD)
|
||||
CC ?= cc
|
||||
CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes -I /usr/local/include
|
||||
CXXFLAGS ?= -O3 -finline-functions -Wall
|
||||
LDFLAGS ?= -fPIC -L /usr/local/lib
|
||||
else ifeq ($(UNAME_SYS), Linux)
|
||||
CC ?= gcc
|
||||
CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
|
||||
CXXFLAGS ?= -O3 -finline-functions -Wall
|
||||
else ifeq ($(UNAME_SYS), SunOS)
|
||||
CC = gcc
|
||||
CFLAGS ?= -m64 -I/opt/local/include -O2 -std=c99 -finline-functions -Wall -Wmissing-prototypes
|
||||
CXXFALGS ?= -O2 -finline-function -Wall
|
||||
LDFLAGS ?= -m64 -fPIC -L /opt/local/lib
|
||||
endif
|
||||
|
||||
CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR)
|
||||
CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR)
|
||||
|
||||
LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei -lsodium
|
||||
LDFLAGS += -shared
|
||||
|
||||
# Verbosity.
|
||||
|
||||
c_verbose_0 = @echo " C " $(?F);
|
||||
c_verbose = $(c_verbose_$(V))
|
||||
|
||||
cpp_verbose_0 = @echo " CPP " $(?F);
|
||||
cpp_verbose = $(cpp_verbose_$(V))
|
||||
|
||||
link_verbose_0 = @echo " LD " $(@F);
|
||||
link_verbose = $(link_verbose_$(V))
|
||||
|
||||
SOURCES := $(shell find $(C_SRC_DIR) -type f \( -name "*.c" -o -name "*.C" -o -name "*.cc" -o -name "*.cpp" \))
|
||||
OBJECTS = $(addsuffix .o, $(basename $(SOURCES)))
|
||||
|
||||
COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c
|
||||
COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c
|
||||
|
||||
$(C_SRC_OUTPUT): $(OBJECTS)
|
||||
$(link_verbose) $(CC) $(OBJECTS) $(LDFLAGS) $(LDLIBS) -o $(C_SRC_OUTPUT)
|
||||
|
||||
%.o: %.c
|
||||
$(COMPILE_C) $(OUTPUT_OPTION) $<
|
||||
|
||||
%.o: %.cc
|
||||
$(COMPILE_CPP) $(OUTPUT_OPTION) $<
|
||||
|
||||
%.o: %.C
|
||||
$(COMPILE_CPP) $(OUTPUT_OPTION) $<
|
||||
|
||||
%.o: %.cpp
|
||||
$(COMPILE_CPP) $(OUTPUT_OPTION) $<
|
||||
|
||||
clean:
|
||||
@rm -f $(C_SRC_OUTPUT) $(OBJECTS)
|
242
c_src/aead.c
Normal file
242
c_src/aead.c
Normal file
@ -0,0 +1,242 @@
|
||||
#include <sodium.h>
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
#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;
|
||||
}
|
46
c_src/aead.h
Normal file
46
c_src/aead.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef ENACL_AEAD_H
|
||||
#define ENACL_AEAD_H
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
/* 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
|
16
c_src/enacl.c
Normal file
16
c_src/enacl.c
Normal file
@ -0,0 +1,16 @@
|
||||
#include <erl_nif.h>
|
||||
|
||||
#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"));
|
||||
}
|
15
c_src/enacl.h
Normal file
15
c_src/enacl.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef ENACL_H
|
||||
#define ENACL_H
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
#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
|
86
c_src/enacl_ext.c
Normal file
86
c_src/enacl_ext.c
Normal file
@ -0,0 +1,86 @@
|
||||
#include <sodium.h>
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
#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);
|
||||
}
|
9
c_src/enacl_ext.h
Normal file
9
c_src/enacl_ext.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef ENACL_EXT_H
|
||||
#define ENACL_EXT_H
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
ERL_NIF_TERM enif_scramble_block_16(ErlNifEnv *env, int argc,
|
||||
ERL_NIF_TERM const argv[]);
|
||||
|
||||
#endif
|
1507
c_src/enacl_nif.c
1507
c_src/enacl_nif.c
File diff suppressed because it is too large
Load Diff
301
c_src/generichash.c
Normal file
301
c_src/generichash.c
Normal file
@ -0,0 +1,301 @@
|
||||
#include <sodium.h>
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
#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;
|
||||
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;
|
||||
}
|
32
c_src/generichash.h
Normal file
32
c_src/generichash.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef ENACL_GENERICHASH_H
|
||||
#define ENACL_GENERICHASH_H
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
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
|
62
c_src/hash.c
Normal file
62
c_src/hash.c
Normal file
@ -0,0 +1,62 @@
|
||||
#include <sodium.h>
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
#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;
|
||||
}
|
17
c_src/hash.h
Normal file
17
c_src/hash.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef ENACL_HASH_H
|
||||
#define ENACL_HASH_H
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
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
|
59
c_src/kdf.c
Normal file
59
c_src/kdf.c
Normal file
@ -0,0 +1,59 @@
|
||||
#include <sodium.h>
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
#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;
|
||||
ErlNifUInt64 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);
|
||||
}
|
||||
|
15
c_src/kdf.h
Normal file
15
c_src/kdf.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef ENACL_KDF_H
|
||||
#define ENACL_KDF_H
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
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
|
151
c_src/kx.c
Normal file
151
c_src/kx.c
Normal file
@ -0,0 +1,151 @@
|
||||
#include <sodium.h>
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
#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;
|
||||
}
|
24
c_src/kx.h
Normal file
24
c_src/kx.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef ENACL_KX_H
|
||||
#define ENACL_KX_H
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
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
|
286
c_src/public.c
Normal file
286
c_src/public.c
Normal file
@ -0,0 +1,286 @@
|
||||
#include <sodium.h>
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
#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 err;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
49
c_src/public.h
Normal file
49
c_src/public.h
Normal file
@ -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
|
180
c_src/pwhash.c
Normal file
180
c_src/pwhash.c
Normal file
@ -0,0 +1,180 @@
|
||||
#include <sodium.h>
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
#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;
|
||||
}
|
18
c_src/pwhash.h
Normal file
18
c_src/pwhash.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef ENACL_PWHASH_H
|
||||
#define ENACL_PWHASH_H
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
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
|
49
c_src/randombytes.c
Normal file
49
c_src/randombytes.c
Normal file
@ -0,0 +1,49 @@
|
||||
#include <sodium.h>
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
#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);
|
||||
}
|
15
c_src/randombytes.h
Normal file
15
c_src/randombytes.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef ENACL_RANDOMBYTES_H
|
||||
#define ENACL_RANDOMBYTES_H
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
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
|
382
c_src/secret.c
Normal file
382
c_src/secret.c
Normal file
@ -0,0 +1,382 @@
|
||||
#include <sodium.h>
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
#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_secretbox_easy(ErlNifEnv *env, int argc,
|
||||
ERL_NIF_TERM const argv[]) {
|
||||
ErlNifBinary key, nonce, msg, cipherbox;
|
||||
|
||||
if ((argc != 3) ||
|
||||
(!enif_inspect_iolist_as_binary(env, argv[0], &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)) {
|
||||
return enif_make_badarg(env);
|
||||
}
|
||||
|
||||
if (!enif_alloc_binary(msg.size + crypto_secretbox_MACBYTES, &cipherbox)) {
|
||||
return enacl_internal_error(env);
|
||||
}
|
||||
|
||||
crypto_secretbox_easy(cipherbox.data, msg.data, msg.size,
|
||||
nonce.data, key.data);
|
||||
|
||||
return enif_make_binary(env, &cipherbox);
|
||||
}
|
||||
|
||||
ERL_NIF_TERM enacl_crypto_secretbox_open_easy(ErlNifEnv *env, int argc,
|
||||
ERL_NIF_TERM const argv[]) {
|
||||
ErlNifBinary key, nonce, cipherbox, msg;
|
||||
|
||||
if ((argc != 3) ||
|
||||
(!enif_inspect_iolist_as_binary(env, argv[0], &cipherbox)) ||
|
||||
(!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) ||
|
||||
(cipherbox.size < crypto_secretbox_MACBYTES)) {
|
||||
return enif_make_badarg(env);
|
||||
}
|
||||
|
||||
if (!enif_alloc_binary(cipherbox.size - crypto_secretbox_MACBYTES, &msg)) {
|
||||
return enacl_internal_error(env);
|
||||
}
|
||||
|
||||
if (crypto_secretbox_open_easy(msg.data, cipherbox.data, cipherbox.size,
|
||||
nonce.data, key.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);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
76
c_src/secret.h
Normal file
76
c_src/secret.h
Normal file
@ -0,0 +1,76 @@
|
||||
#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_secretbox_easy(ErlNifEnv *env, int argc,
|
||||
ERL_NIF_TERM const argv[]);
|
||||
|
||||
ERL_NIF_TERM enacl_crypto_secretbox_open_easy(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
|
462
c_src/secretstream.c
Normal file
462
c_src/secretstream.c
Normal file
@ -0,0 +1,462 @@
|
||||
#include <erl_nif.h>
|
||||
#include <sodium.h>
|
||||
|
||||
#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) {
|
||||
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_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;
|
||||
}
|
78
c_src/secretstream.h
Normal file
78
c_src/secretstream.h
Normal file
@ -0,0 +1,78 @@
|
||||
#ifndef ENACL_SECRETSTREAM_H
|
||||
#define ENACL_SECRETSTREAM_H
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
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
|
488
c_src/sign.c
Normal file
488
c_src/sign.c
Normal file
@ -0,0 +1,488 @@
|
||||
#include <sodium.h>
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
#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) {
|
||||
ret = enacl_internal_error(env);
|
||||
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:
|
||||
ret = enacl_internal_error(env);
|
||||
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);
|
||||
}
|
||||
|
||||
if (crypto_sign_ed25519_pk_to_curve25519(curve25519_pk.data, ed25519_pk.data) != 0) {
|
||||
return enacl_internal_error(env);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
70
c_src/sign.h
Normal file
70
c_src/sign.h
Normal file
@ -0,0 +1,70 @@
|
||||
#ifndef ENACL_SIGN_H
|
||||
#define ENACL_SIGN_H
|
||||
|
||||
#include <erl_nif.h>
|
||||
|
||||
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
|
@ -1,3 +1,6 @@
|
||||
eqc_compile:
|
||||
$(MAKE) -C .. eqc_compile
|
||||
|
||||
console:
|
||||
erl -pa ../ebin ../deps/*/ebin
|
||||
|
||||
|
@ -1,8 +1,22 @@
|
||||
-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}).
|
||||
-define(FAULT(Arg1, Arg2), fault(Arg1, Arg2)).
|
||||
-define(FAULT_RATE(Arg1, Arg2, Arg3), fault_rate(Arg1, Arg2, Arg3)).
|
||||
-else.
|
||||
-define(FAULT(Arg1, Arg2), noop_fault(Arg1, Arg2)).
|
||||
-define(FAULT_RATE(Arg1, Arg2, Arg3), noop_fault_rate(Arg1, Arg2, Arg3)).
|
||||
-endif.
|
||||
|
||||
start()->
|
||||
eqc:module(?MODULE).
|
||||
|
||||
noop_fault(_Bad, Good) -> Good.
|
||||
|
||||
noop_fault_rate(_1, _2, Gen) -> Gen.
|
||||
|
||||
non_byte_int() ->
|
||||
oneof([
|
||||
@ -14,7 +28,7 @@ g_iolist() ->
|
||||
?SIZED(Sz, g_iolist(Sz)).
|
||||
|
||||
g_iolist(0) ->
|
||||
fault(
|
||||
?FAULT(
|
||||
oneof([
|
||||
elements([a,b,c]),
|
||||
real(),
|
||||
@ -22,7 +36,7 @@ g_iolist(0) ->
|
||||
]),
|
||||
return([]));
|
||||
g_iolist(N) ->
|
||||
fault(
|
||||
?FAULT(
|
||||
oneof([
|
||||
elements([a,b,c]),
|
||||
real(),
|
||||
@ -34,7 +48,7 @@ g_iolist(N) ->
|
||||
])).
|
||||
|
||||
g_iodata() ->
|
||||
fault(
|
||||
?FAULT(
|
||||
oneof([elements([a,b,c]), real()]),
|
||||
oneof([binary(), g_iolist(), eqc_gen:largebinary(64*1024)])).
|
||||
|
||||
@ -50,7 +64,7 @@ v_iodata(Structure) -> v_iolist(Structure).
|
||||
|
||||
%% Generator for binaries of a given size with different properties and fault injection:
|
||||
g_binary(Sz) ->
|
||||
fault(g_binary_bad(Sz), g_binary_good(Sz)).
|
||||
?FAULT(g_binary_bad(Sz), g_binary_good(Sz)).
|
||||
|
||||
g_binary_good(Sz) when Sz =< 32 -> binary(Sz);
|
||||
g_binary_good(Sz) -> eqc_gen:largebinary(Sz).
|
||||
@ -69,12 +83,12 @@ 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() ->
|
||||
fault(g_nat_bad(), nat()).
|
||||
?FAULT(g_nat_bad(), nat()).
|
||||
|
||||
g_nat_bad() ->
|
||||
oneof([
|
||||
@ -97,23 +111,56 @@ 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).
|
||||
|
||||
keypair() ->
|
||||
fault(keypair_bad(), keypair_good()).
|
||||
?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
|
||||
%% * box_open/4
|
||||
%% * box_beforenm/2
|
||||
%% * 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.
|
||||
|
||||
@ -158,10 +205,10 @@ failure(X) -> {failure, X}.
|
||||
|
||||
prop_box_correct() ->
|
||||
?FORALL({Msg, Nonce, {PK1, SK1}, {PK2, SK2}},
|
||||
{fault_rate(1, 40, g_iodata()),
|
||||
fault_rate(1, 40, nonce()),
|
||||
fault_rate(1, 40, keypair()),
|
||||
fault_rate(1, 40, keypair())},
|
||||
{?FAULT_RATE(1, 40, g_iodata()),
|
||||
?FAULT_RATE(1, 40, nonce()),
|
||||
?FAULT_RATE(1, 40, keypair()),
|
||||
?FAULT_RATE(1, 40, keypair())},
|
||||
begin
|
||||
case v_iodata(Msg) andalso nonce_valid(Nonce) andalso keypair_valid(PK1, SK1) andalso keypair_valid(PK2, SK2) of
|
||||
true ->
|
||||
@ -182,10 +229,10 @@ prop_box_correct() ->
|
||||
|
||||
prop_box_failure_integrity() ->
|
||||
?FORALL({Msg, Nonce, {PK1, SK1}, {PK2, SK2}},
|
||||
{fault_rate(1, 40, g_iodata()),
|
||||
fault_rate(1, 40, nonce()),
|
||||
fault_rate(1, 40, keypair()),
|
||||
fault_rate(1, 40, keypair())},
|
||||
{?FAULT_RATE(1, 40, g_iodata()),
|
||||
?FAULT_RATE(1, 40, nonce()),
|
||||
?FAULT_RATE(1, 40, keypair()),
|
||||
?FAULT_RATE(1, 40, keypair())},
|
||||
begin
|
||||
case v_iodata(Msg)
|
||||
andalso nonce_valid(Nonce)
|
||||
@ -206,44 +253,10 @@ prop_box_failure_integrity() ->
|
||||
end
|
||||
end).
|
||||
|
||||
prop_seal_box_failure_integrity() ->
|
||||
?FORALL({Msg, {PK1, SK1}}, {fault_rate(1,40,g_iodata()), fault_rate(1,40,keypair())},
|
||||
begin
|
||||
case v_iodata(Msg) andalso keypair_valid(PK1, SK1) of
|
||||
true ->
|
||||
CT = enacl:box_seal(Msg, PK1),
|
||||
Err = enacl:box_seal_open([<<"x">>, CT], PK1, SK1),
|
||||
equals(Err, {error, failed_verification});
|
||||
false ->
|
||||
case box_seal(Msg, PK1) of
|
||||
badarg -> true;
|
||||
Res ->
|
||||
failure(box_seal_open(Res, PK1, SK1))
|
||||
end
|
||||
end
|
||||
end).
|
||||
|
||||
prop_seal_box_correct() ->
|
||||
?FORALL({Msg, {PK1, SK1}},
|
||||
{fault_rate(1, 40, g_iodata()),
|
||||
fault_rate(1, 40, keypair())},
|
||||
begin
|
||||
case v_iodata(Msg) andalso keypair_valid(PK1, SK1) of
|
||||
true ->
|
||||
SealedCipherText = enacl:box_seal(Msg, PK1),
|
||||
{ok, DecodedMsg} = enacl:box_seal_open(SealedCipherText, PK1, SK1),
|
||||
equals(iolist_to_binary(Msg), DecodedMsg);
|
||||
false ->
|
||||
case box_seal(Msg, PK1) of
|
||||
badarg -> true;
|
||||
Res -> failure(box_seal_open(Res, PK1, SK1))
|
||||
end
|
||||
end
|
||||
end).
|
||||
|
||||
%% PRECOMPUTATIONS
|
||||
beforenm_key() ->
|
||||
?LET([{PK1, SK1}, {PK2, SK2}], [fault_rate(1, 40, keypair()), fault_rate(1, 40, keypair())],
|
||||
?LET([{PK1, SK1}, {PK2, SK2}], [?FAULT_RATE(1, 40, keypair()), ?FAULT_RATE(1, 40, keypair())],
|
||||
case keypair_valid(PK1, SK1) andalso keypair_valid(PK2, SK2) of
|
||||
true ->
|
||||
enacl:box_beforenm(PK1, SK2);
|
||||
@ -251,15 +264,15 @@ 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() ->
|
||||
?FORALL([{PK1, SK1}, {PK2, SK2}], [fault_rate(1, 40, keypair()), fault_rate(1, 40, keypair())],
|
||||
?FORALL([{PK1, SK1}, {PK2, SK2}], [?FAULT_RATE(1, 40, keypair()), ?FAULT_RATE(1, 40, keypair())],
|
||||
case keypair_valid(PK1, SK1) andalso keypair_valid(PK2, SK2) of
|
||||
true ->
|
||||
equals(enacl:box_beforenm(PK1, SK2), enacl:box_beforenm(PK2, SK1));
|
||||
@ -272,9 +285,9 @@ prop_beforenm_correct() ->
|
||||
|
||||
prop_afternm_correct() ->
|
||||
?FORALL([Msg, Nonce, Key],
|
||||
[fault_rate(1, 40, g_iodata()),
|
||||
fault_rate(1, 40, nonce()),
|
||||
fault_rate(1, 40, beforenm_key())],
|
||||
[?FAULT_RATE(1, 40, g_iodata()),
|
||||
?FAULT_RATE(1, 40, nonce()),
|
||||
?FAULT_RATE(1, 40, beforenm_key())],
|
||||
begin
|
||||
case v_iodata(Msg) andalso nonce_valid(Nonce) andalso v_key(Key) of
|
||||
true ->
|
||||
@ -311,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
|
||||
@ -325,16 +338,16 @@ sign_keypair_good() ->
|
||||
return(enacl:sign_keypair()).
|
||||
|
||||
sign_keypair() ->
|
||||
fault(sign_keypair_bad(), sign_keypair_good()).
|
||||
?FAULT(sign_keypair_bad(), sign_keypair_good()).
|
||||
|
||||
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) ->
|
||||
@ -342,8 +355,8 @@ sign_keypair_valid(KP) ->
|
||||
|
||||
prop_sign_detached() ->
|
||||
?FORALL({Msg, KeyPair},
|
||||
{fault_rate(1, 40, g_iodata()),
|
||||
fault_rate(1, 40, sign_keypair())},
|
||||
{?FAULT_RATE(1, 40, g_iodata()),
|
||||
?FAULT_RATE(1, 40, sign_keypair())},
|
||||
begin
|
||||
case v_iodata(Msg) andalso sign_keypair_secret_valid(KeyPair) of
|
||||
true ->
|
||||
@ -358,8 +371,8 @@ prop_sign_detached() ->
|
||||
|
||||
prop_sign() ->
|
||||
?FORALL({Msg, KeyPair},
|
||||
{fault_rate(1, 40, g_iodata()),
|
||||
fault_rate(1, 40, sign_keypair())},
|
||||
{?FAULT_RATE(1, 40, g_iodata()),
|
||||
?FAULT_RATE(1, 40, sign_keypair())},
|
||||
begin
|
||||
case v_iodata(Msg) andalso sign_keypair_secret_valid(KeyPair) of
|
||||
true ->
|
||||
@ -395,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)).
|
||||
?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;
|
||||
@ -419,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)
|
||||
@ -442,64 +455,78 @@ prop_sign_open() ->
|
||||
badargs(fun() -> enacl:sign_open(SignMsg, PK) end)
|
||||
end)).
|
||||
|
||||
prop_seal_box_failure_integrity() ->
|
||||
?FORALL({Msg, {PK1, SK1}}, {?FAULT_RATE(1,40,g_iodata()), ?FAULT_RATE(1,40,keypair())},
|
||||
begin
|
||||
case v_iodata(Msg) andalso keypair_valid(PK1, SK1) of
|
||||
true ->
|
||||
CT = enacl:box_seal(Msg, PK1),
|
||||
Err = enacl:box_seal_open([<<"x">>, CT], PK1, SK1),
|
||||
equals(Err, {error, failed_verification});
|
||||
false ->
|
||||
case box_seal(Msg, PK1) of
|
||||
badarg -> true;
|
||||
Res ->
|
||||
failure(box_seal_open(Res, PK1, SK1))
|
||||
end
|
||||
end
|
||||
end).
|
||||
|
||||
prop_seal_box_correct() ->
|
||||
?FORALL({Msg, {PK1, SK1}},
|
||||
{?FAULT_RATE(1, 40, g_iodata()),
|
||||
?FAULT_RATE(1, 40, keypair())},
|
||||
begin
|
||||
case v_iodata(Msg) andalso keypair_valid(PK1, SK1) of
|
||||
true ->
|
||||
SealedCipherText = enacl:box_seal(Msg, PK1),
|
||||
{ok, DecodedMsg} = enacl:box_seal_open(SealedCipherText, PK1, SK1),
|
||||
equals(iolist_to_binary(Msg), DecodedMsg);
|
||||
false ->
|
||||
case box_seal(Msg, PK1) of
|
||||
badarg -> true;
|
||||
Res -> failure(box_seal_open(Res, PK1, SK1))
|
||||
end
|
||||
end
|
||||
end).
|
||||
|
||||
|
||||
%% CRYPTO SECRET BOX
|
||||
%% -------------------------------
|
||||
|
||||
%% Note: key sizes are the same in a lot of situations, so we can use the same generator
|
||||
%% for keys in many locations.
|
||||
|
||||
key_sz(Sz) ->
|
||||
equals(enacl:secretbox_key_size(), Sz).
|
||||
|
||||
prop_key_sizes() ->
|
||||
conjunction([{secret, key_sz(enacl:secretbox_key_size())},
|
||||
{stream, key_sz(enacl:stream_key_size())},
|
||||
{auth, key_sz(enacl:auth_key_size())},
|
||||
{onetimeauth, key_sz(enacl:onetime_auth_key_size())}]).
|
||||
|
||||
nonce_sz(Sz) ->
|
||||
equals(enacl:secretbox_nonce_size(), Sz).
|
||||
|
||||
prop_nonce_sizes() ->
|
||||
conjunction([{secret, nonce_sz(enacl:secretbox_nonce_size())},
|
||||
{stream, nonce_sz(enacl:stream_nonce_size())}]).
|
||||
|
||||
%% ------------------------------------------------------------
|
||||
%% * 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()).
|
||||
?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.
|
||||
|
||||
secretbox(Msg, Nonce, Key) ->
|
||||
try
|
||||
enacl:secretbox(Msg, Nonce, Key)
|
||||
catch
|
||||
error:badarg -> badarg
|
||||
try enacl:secretbox(Msg, Nonce, Key)
|
||||
catch error:badarg -> badarg
|
||||
end.
|
||||
|
||||
secretbox_open(Msg, Nonce, Key) ->
|
||||
try
|
||||
enacl:secretbox_open(Msg, Nonce, Key)
|
||||
catch
|
||||
error:badarg -> badarg
|
||||
try enacl:secretbox_open(Msg, Nonce, Key)
|
||||
catch error:badarg -> badarg
|
||||
end.
|
||||
|
||||
prop_secretbox_correct() ->
|
||||
?FORALL({Msg, Nonce, Key},
|
||||
{fault_rate(1, 40, g_iodata()),
|
||||
fault_rate(1, 40, nonce()),
|
||||
fault_rate(1, 40, secret_key())},
|
||||
{?FAULT_RATE(1, 40, g_iodata()),
|
||||
?FAULT_RATE(1, 40, nonce()),
|
||||
?FAULT_RATE(1, 40, secret_key())},
|
||||
begin
|
||||
case v_iodata(Msg) andalso nonce_valid(Nonce) andalso secret_key_valid(Key) of
|
||||
true ->
|
||||
@ -523,12 +550,100 @@ prop_secretbox_failure_integrity() ->
|
||||
equals(Err, {error, failed_verification})
|
||||
end).
|
||||
|
||||
secretbox_easy(Msg, Nonce, Key) ->
|
||||
try enacl:secretbox_easy(Msg, Nonce, Key)
|
||||
catch error:badarg -> badarg
|
||||
end.
|
||||
|
||||
secretbox_open_easy(Msg, Nonce, Key) ->
|
||||
try enacl:secretbox_open_easy(Msg, Nonce, Key)
|
||||
catch error:badarg -> badarg
|
||||
end.
|
||||
|
||||
prop_secretbox_easy_correct() ->
|
||||
?FORALL({Msg, Nonce, Key},
|
||||
{?FAULT_RATE(1, 40, g_iodata()),
|
||||
?FAULT_RATE(1, 40, nonce()),
|
||||
?FAULT_RATE(1, 40, secret_key())},
|
||||
begin
|
||||
case v_iodata(Msg) andalso nonce_valid(Nonce) andalso secret_key_valid(Key) of
|
||||
true ->
|
||||
CipherText = enacl:secretbox_easy(Msg, Nonce, Key),
|
||||
{ok, DecodedMsg} = enacl:secretbox_open_easy(CipherText, Nonce, Key),
|
||||
equals(iolist_to_binary(Msg), DecodedMsg);
|
||||
false ->
|
||||
case secretbox_easy(Msg, Nonce, Key) of
|
||||
badarg -> true;
|
||||
Res ->
|
||||
failure(secretbox_open_easy(Res, Nonce, Key))
|
||||
end
|
||||
end
|
||||
end).
|
||||
|
||||
prop_secretbox_easy_failure_integrity() ->
|
||||
?FORALL({Msg, Nonce, Key}, {g_iodata(), nonce(), secret_key()},
|
||||
begin
|
||||
CipherText = enacl:secretbox_easy(Msg, Nonce, Key),
|
||||
Err = enacl:secretbox_open_easy([<<"x">>, CipherText], Nonce, Key),
|
||||
equals(Err, {error, failed_verification})
|
||||
end).
|
||||
|
||||
%% AEAD ChaCha20Poly1305
|
||||
%% ------------------------------------------------------------
|
||||
%% * aead_chacha20poly1305_encrypt/4,
|
||||
%% * aead_chacha20poly1305_decrypt/4,
|
||||
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)), binary(NPubBytes)},
|
||||
begin
|
||||
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_ietf_fail() ->
|
||||
NPubBytes = enacl:aead_chacha20poly1305_ietf_NPUBBYTES(),
|
||||
?FORALL({Key, Msg, AD, Nonce},
|
||||
{binary(32), binary(), ?LET(ADBytes, choose(0,16), binary(ADBytes)), binary(NPubBytes)},
|
||||
begin
|
||||
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
|
||||
end).
|
||||
|
||||
%% CRYPTO STREAM
|
||||
%% ------------------------------------------------------------
|
||||
%% * stream/3
|
||||
prop_stream_correct() ->
|
||||
?FORALL({Len, Nonce, Key},
|
||||
{int(),
|
||||
fault_rate(1, 40, nonce()),
|
||||
fault_rate(1, 40, secret_key())},
|
||||
?FAULT_RATE(1, 40, nonce()),
|
||||
?FAULT_RATE(1, 40, secret_key())},
|
||||
case Len >= 0 andalso nonce_valid(Nonce) andalso secret_key_valid(Key) of
|
||||
true ->
|
||||
CipherStream = enacl:stream(Len, Nonce, Key),
|
||||
@ -541,29 +656,35 @@ xor_bytes(<<A, As/binary>>, <<B, Bs/binary>>) ->
|
||||
[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
|
||||
%% ------------------------------------------------------------
|
||||
%% * auth/2
|
||||
%% * auth_verify/3
|
||||
prop_auth_correct() ->
|
||||
?FORALL({Msg, Key},
|
||||
{fault_rate(1, 40, g_iodata()),
|
||||
fault_rate(1, 40, secret_key())},
|
||||
{?FAULT_RATE(1, 40, g_iodata()),
|
||||
?FAULT_RATE(1, 40, secret_key())},
|
||||
case v_iodata(Msg) andalso secret_key_valid(Key) of
|
||||
true ->
|
||||
Authenticator = enacl:auth(Msg, Key),
|
||||
@ -573,22 +694,22 @@ 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)).
|
||||
?FAULT(authenticator_bad(), authenticator_good(Msg, Key)).
|
||||
|
||||
authenticator_valid({valid, _}) -> true;
|
||||
authenticator_valid({invalid, _}) -> true;
|
||||
@ -596,8 +717,8 @@ authenticator_valid(_) -> false.
|
||||
|
||||
prop_auth_verify_correct() ->
|
||||
?FORALL({Msg, Key},
|
||||
{fault_rate(1, 40, g_iodata()),
|
||||
fault_rate(1, 40, secret_key())},
|
||||
{?FAULT_RATE(1, 40, g_iodata()),
|
||||
?FAULT_RATE(1, 40, secret_key())},
|
||||
?FORALL(Authenticator, authenticator(Msg, Key),
|
||||
case v_iodata(Msg) andalso secret_key_valid(Key) andalso authenticator_valid(Authenticator) of
|
||||
true ->
|
||||
@ -612,10 +733,13 @@ prop_auth_verify_correct() ->
|
||||
end)).
|
||||
|
||||
%% CRYPTO ONETIME AUTH
|
||||
%% ------------------------------------------------------------
|
||||
%% * onetime_auth/2
|
||||
%% * onetime_auth_verify/3
|
||||
prop_onetimeauth_correct() ->
|
||||
?FORALL({Msg, Key},
|
||||
{fault_rate(1, 40, g_iodata()),
|
||||
fault_rate(1, 40, secret_key())},
|
||||
{?FAULT_RATE(1, 40, g_iodata()),
|
||||
?FAULT_RATE(1, 40, secret_key())},
|
||||
case v_iodata(Msg) andalso secret_key_valid(Key) of
|
||||
true ->
|
||||
Authenticator = enacl:onetime_auth(Msg, Key),
|
||||
@ -625,22 +749,22 @@ 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)).
|
||||
?FAULT(ot_authenticator_bad(), ot_authenticator_good(Msg, Key)).
|
||||
|
||||
ot_authenticator_valid({valid, _}) -> true;
|
||||
ot_authenticator_valid({invalid, _}) -> true;
|
||||
@ -648,8 +772,8 @@ ot_authenticator_valid(_) -> false.
|
||||
|
||||
prop_onetime_auth_verify_correct() ->
|
||||
?FORALL({Msg, Key},
|
||||
{fault_rate(1, 40, g_iodata()),
|
||||
fault_rate(1, 40, secret_key())},
|
||||
{?FAULT_RATE(1, 40, g_iodata()),
|
||||
?FAULT_RATE(1, 40, secret_key())},
|
||||
?FORALL(Authenticator, ot_authenticator(Msg, Key),
|
||||
case v_iodata(Msg) andalso secret_key_valid(Key) andalso ot_authenticator_valid(Authenticator) of
|
||||
true ->
|
||||
@ -663,7 +787,71 @@ prop_onetime_auth_verify_correct() ->
|
||||
badargs(fun() -> enacl:onetime_auth_verify(Authenticator, Msg, Key) end)
|
||||
end)).
|
||||
|
||||
%% HASHING
|
||||
%% PWHASH
|
||||
%% -------------------------------
|
||||
%% * pwhash/2
|
||||
%% * pwhash_str/1
|
||||
%% * pwhash_str_verify/2
|
||||
pwhash(Passwd, Salt) ->
|
||||
try
|
||||
enacl:pwhash(Passwd, Salt)
|
||||
catch
|
||||
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)
|
||||
catch
|
||||
error:badarg -> badarg
|
||||
end.
|
||||
|
||||
pwhash_str_verify(PasswdHash, Passwd) ->
|
||||
try
|
||||
enacl:pwhash_str_verify(PasswdHash, Passwd)
|
||||
catch
|
||||
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, OLimit, MLimit},
|
||||
{?FAULT_RATE(1, 40, g_iodata()),
|
||||
elements([interactive, moderate]),
|
||||
elements([interactive, moderate])},
|
||||
begin
|
||||
case v_iodata(Passwd) of
|
||||
true ->
|
||||
Ascii = enacl:pwhash_str(Passwd, OLimit, MLimit),
|
||||
S = enacl:pwhash_str_verify(Ascii, Passwd),
|
||||
equals(S, true);
|
||||
false ->
|
||||
badargs(fun() -> enacl:pwhash_str(Passwd) end),
|
||||
badargs(fun() -> enacl:pwhash_str_verify("", Passwd) end)
|
||||
end
|
||||
end).
|
||||
|
||||
%% SUBTLE HASHING
|
||||
%% ---------------------------
|
||||
diff_pair() ->
|
||||
?SUCHTHAT({X, Y}, {g_iodata(), g_iodata()},
|
||||
@ -688,9 +876,45 @@ 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,
|
||||
%% * verify_32/2
|
||||
verify_pair_bad(Sz) ->
|
||||
?LET(X, elements([fst, snd]),
|
||||
case X of
|
||||
@ -706,7 +930,7 @@ verify_pair_good(Sz) ->
|
||||
?SUCHTHAT({X, Y}, {binary(Sz), binary(Sz)}, X /= Y)]).
|
||||
|
||||
verify_pair(Sz) ->
|
||||
fault(verify_pair_bad(Sz), verify_pair_good(Sz)).
|
||||
?FAULT(verify_pair_bad(Sz), verify_pair_good(Sz)).
|
||||
|
||||
verify_pair_valid(Sz, X, Y) ->
|
||||
byte_size(X) == Sz andalso byte_size(Y) == Sz.
|
||||
@ -740,11 +964,14 @@ prop_verify_32() ->
|
||||
end).
|
||||
|
||||
%% RANDOMBYTES
|
||||
%% ------------------------------------------------------------
|
||||
%% * randombytes/1
|
||||
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),
|
||||
@ -755,12 +982,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(),
|
||||
|
@ -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(),
|
||||
@ -22,3 +23,7 @@ prop_shared_secret() ->
|
||||
equals(Alice, Bob)
|
||||
end).
|
||||
|
||||
prop_scramble_block() ->
|
||||
?FORALL({Block, Key}, {binary(16), eqc_gen:largebinary(32)},
|
||||
is_binary(enacl_ext:scramble_block_16(Block, Key))).
|
||||
|
||||
|
42
rebar.config
42
rebar.config
@ -1,9 +1,39 @@
|
||||
{erl_opts, [debug_info]}.
|
||||
|
||||
{pre_hooks, [{"freebsd", compile, "gmake -C c_src"},
|
||||
{"freebsd", clean, "gmake -C c_src clean"},
|
||||
{"netbsd", compile, "gmake -C c_src"},
|
||||
{"netbsd", clean, "gmake -C c_src clean"},
|
||||
{"(linux|darwin|solaris)", compile, "make -C c_src"},
|
||||
{"(linux|darwin|solaris)", clean, "make -C c_src clean"}
|
||||
{plugins, [pc]}.
|
||||
|
||||
{project_plugins, [rebar3_hex]}.
|
||||
|
||||
{provider_hooks, [
|
||||
{pre, [
|
||||
{compile, {pc, compile}},
|
||||
{clean, {pc, clean}}
|
||||
]}
|
||||
]}.
|
||||
|
||||
{port_specs, [
|
||||
{"priv/enacl_nif.so", [
|
||||
"c_src/*.c"
|
||||
]}
|
||||
]}.
|
||||
|
||||
{port_env, [
|
||||
{"darwin", "CFLAGS", "$CFLAGS -fPIC -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes"},
|
||||
{"darwin", "CXXFLAGS", "$CXXFLAGS -fPIC -O3 -finline-functions -Wall"},
|
||||
{"darwin", "LDFLAGS", "$LDFLAGS -flat_namespace -undefined suppress -lsodium"},
|
||||
|
||||
{"linux", "CFLAGS", "$CFLAGS -fPIC -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes"},
|
||||
{"linux", "CXXFLAGS", "$CXXFLAGS -fPIC -O3 -finline-functions -Wall"},
|
||||
{"linux", "LDFLAGS", "$LDFLAGS -lsodium"},
|
||||
|
||||
{"freebsd", "CFLAGS", "$CFLAGS -fPIC -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes -I /usr/local/include"},
|
||||
{"freebsd", "CXXFLAGS", "$CXXFLAGS -fPIC -O3 -finline-functions -Wall"},
|
||||
{"freebsd", "LDFLAGS", "$LDFLAGS -fPIC -L /usr/local/lib -lsodium"},
|
||||
|
||||
{"solaris", "CFLAGS", "$CFLAGS -fPIC -m64 -I/opt/local/include -O2 -std=c99 -finline-functions -Wall -Wmissing-prototypes"},
|
||||
{"solaris", "CXXFLAGS", "$CXXFLAGS -fPIC -O2 -finline-function -Wall"},
|
||||
{"solaris", "LDFLAGS", "$LDFLAGS -m64 -fPIC -L /opt/local/lib -lsodium"},
|
||||
|
||||
{"win32", "CFLAGS", "$CFLAGS /LD /O2 /DNDEBUG"},
|
||||
{"win32", "LDFLAGS", "$LDFLAGS libsodium.dll.a"}
|
||||
]}.
|
||||
|
12
shell.nix
Normal file
12
shell.nix
Normal file
@ -0,0 +1,12 @@
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
pkgs.mkShell {
|
||||
buildInputs = [
|
||||
pkgs.hello
|
||||
|
||||
# keep this line if you use bash
|
||||
pkgs.bashInteractive
|
||||
pkgs.erlang
|
||||
pkgs.libsodium
|
||||
];
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
{application,enacl,
|
||||
[
|
||||
{description, "Erlang NaCl bindings"},
|
||||
{vsn, "0.16.0"},
|
||||
[{description,"Erlang libsodium (NaCl) bindings"},
|
||||
{vsn,"1.2.1"},
|
||||
{registered,[]},
|
||||
{applications,[kernel,stdlib]},
|
||||
{env, []}
|
||||
]}.
|
||||
{env,[]},
|
||||
{maintainers,["Jesper Louis Andersen"]},
|
||||
{licenses,["MIT","ISC"]},
|
||||
{links,[{"Github","https://github.com/jlouis/enacl"}]}]}.
|
||||
|
934
src/enacl.erl
934
src/enacl.erl
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
@ -50,6 +57,10 @@
|
||||
crypto_secretbox_b/3,
|
||||
crypto_secretbox_open/3,
|
||||
crypto_secretbox_open_b/3,
|
||||
crypto_secretbox_easy/3,
|
||||
crypto_secretbox_easy_b/3,
|
||||
crypto_secretbox_open_easy/3,
|
||||
crypto_secretbox_open_easy_b/3,
|
||||
|
||||
crypto_stream_chacha20_KEYBYTES/0,
|
||||
crypto_stream_chacha20_NONCEBYTES/0,
|
||||
@ -67,6 +78,20 @@
|
||||
crypto_stream_xor/3,
|
||||
crypto_stream_xor_b/3,
|
||||
|
||||
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,
|
||||
|
||||
@ -91,12 +116,14 @@
|
||||
|
||||
%% Curve25519
|
||||
-export([
|
||||
crypto_curve25519_scalarmult/2
|
||||
crypto_curve25519_scalarmult/2,
|
||||
crypto_curve25519_scalarmult_base/1
|
||||
]).
|
||||
|
||||
%% 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,
|
||||
@ -122,9 +149,58 @@
|
||||
sodium_memzero/1
|
||||
]).
|
||||
|
||||
%% Password Hashing - Argon2 Algorithm
|
||||
-export([
|
||||
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,
|
||||
crypto_generichash_BYTES_MIN/0,
|
||||
crypto_generichash_BYTES_MAX/0,
|
||||
crypto_generichash_KEYBYTES/0,
|
||||
crypto_generichash_KEYBYTES_MIN/0,
|
||||
crypto_generichash_KEYBYTES_MAX/0,
|
||||
crypto_generichash/3,
|
||||
crypto_generichash_init/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 :>
|
||||
@ -146,6 +222,43 @@ init() ->
|
||||
SoName = filename:join(Dir, atom_to_list(?MODULE)),
|
||||
erlang:load_nif(SoName, 0).
|
||||
|
||||
crypto_generichash_BYTES() -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_generichash_BYTES_MIN() -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_generichash_BYTES_MAX() -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_generichash_KEYBYTES() -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_generichash_KEYBYTES_MIN() -> erlang:nif_error(nif_not_loaded).
|
||||
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(_HashState, _Message) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_generichash_final(_HashState) -> 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).
|
||||
@ -165,8 +278,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).
|
||||
|
||||
@ -174,6 +289,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).
|
||||
@ -187,6 +307,10 @@ crypto_secretbox(_Msg, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_secretbox_b(_Msg, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_secretbox_open(_Msg, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_secretbox_open_b(_Msg, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_secretbox_easy(_Msg, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_secretbox_easy_b(_Msg, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_secretbox_open_easy(_Msg, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_secretbox_open_easy_b(_Msg, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
|
||||
crypto_stream_chacha20_KEYBYTES() -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_stream_chacha20_NONCEBYTES() -> erlang:nif_error(nif_not_loaded).
|
||||
@ -202,6 +326,20 @@ 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_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).
|
||||
crypto_auth(_Msg, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
@ -221,8 +359,10 @@ crypto_onetimeauth_verify(_Authenticator, _Msg, _Key) -> erlang:nif_error(nif_no
|
||||
crypto_onetimeauth_verify_b(_Authenticator, _Msg, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
|
||||
crypto_curve25519_scalarmult(_Secret, _BasePoint) -> erlang:nif_error(nif_not_loaded).
|
||||
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).
|
||||
@ -242,5 +382,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).
|
||||
|
196
test/enacl_SUITE.erl
Normal file
196
test/enacl_SUITE.erl
Normal file
@ -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, <<PW/binary, 1>>),
|
||||
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.
|
Loading…
x
Reference in New Issue
Block a user