Compare commits
302 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 | ||
![]() |
61be95caad | ||
![]() |
fffe07e965 | ||
![]() |
ec60a63874 | ||
![]() |
2b8052a09a | ||
![]() |
3ba654ab68 | ||
![]() |
2da2ba138f | ||
![]() |
a708d0b11e | ||
![]() |
da64305ee5 | ||
![]() |
32fb4ae148 | ||
![]() |
2ee171bcbf | ||
![]() |
8728c2cc10 | ||
![]() |
1789c6d3c8 | ||
![]() |
3f4b50dbe1 | ||
![]() |
49256985b2 | ||
![]() |
9849e7310a | ||
![]() |
4d5524db61 | ||
![]() |
ca005d32f9 | ||
![]() |
03f93c4f7d | ||
![]() |
01526484f2 | ||
![]() |
b1c3e69fb5 | ||
![]() |
2cad66ac2a | ||
![]() |
2e5163a934 | ||
![]() |
a59e983d9e | ||
![]() |
1562eed9a3 | ||
![]() |
66cf275152 | ||
![]() |
8c567be6a6 | ||
![]() |
a49c5a816c | ||
![]() |
f05ba6e207 | ||
![]() |
8c31db792b | ||
![]() |
4e39bccab6 | ||
![]() |
6bfa191f6b | ||
![]() |
310bb3cc77 | ||
![]() |
a58ab2c193 | ||
![]() |
a135bce9d3 | ||
![]() |
ce5f69f9f7 | ||
![]() |
9830068304 | ||
![]() |
f444d1e4ac | ||
![]() |
4676328efa | ||
![]() |
92109eb354 | ||
![]() |
7b64f3e52b | ||
![]() |
233c924b70 | ||
![]() |
64c03a1425 | ||
![]() |
ed3e31f26f | ||
![]() |
35515e465a | ||
![]() |
7e7ac67fb3 | ||
![]() |
89e28feab8 | ||
![]() |
bec29dfbe1 | ||
![]() |
a7fbfbd53d | ||
![]() |
7bf9f24259 | ||
![]() |
d559c87fa7 | ||
![]() |
de0ce86473 | ||
![]() |
b544a37a72 | ||
![]() |
d34e3bfef1 | ||
![]() |
2979503a7f | ||
![]() |
4a523e3b0b | ||
![]() |
7ba5f9888f | ||
![]() |
3f8979ce64 | ||
![]() |
64832fc95d | ||
![]() |
69051e1803 | ||
![]() |
e408278d50 | ||
![]() |
d61d363426 | ||
![]() |
ef36bb85c1 | ||
![]() |
4e66fc3b94 | ||
![]() |
6de936899c | ||
![]() |
50b0058335 | ||
![]() |
2a23a16ed3 |
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"
|
||||
}]
|
10
.gitignore
vendored
10
.gitignore
vendored
@ -1,8 +1,9 @@
|
||||
.rebar
|
||||
.rebar3
|
||||
.envrc
|
||||
ebin
|
||||
*.beam
|
||||
*.o
|
||||
*.d
|
||||
*.eqc
|
||||
*.so
|
||||
eqc_test/.eqc-info
|
||||
@ -10,3 +11,10 @@ doc/edoc-info
|
||||
doc/*.html
|
||||
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.
|
17
CONTRIBUTORS
Normal file
17
CONTRIBUTORS
Normal file
@ -0,0 +1,17 @@
|
||||
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
|
||||
|
27
Makefile
27
Makefile
@ -1,12 +1,29 @@
|
||||
REBAR=rebar
|
||||
REBAR=rebar3
|
||||
RUN_EQC=erl -pa _build/default/lib/enacl/ebin -noshell -s enacl_eqc -s init stop
|
||||
|
||||
.PHONY: compile
|
||||
compile: deps
|
||||
compile:
|
||||
$(REBAR) compile
|
||||
|
||||
.PHONY: deps
|
||||
deps:
|
||||
$(REBAR) get-deps
|
||||
.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:
|
||||
|
302
README.md
302
README.md
@ -1,138 +1,278 @@
|
||||
# Erlang bindings for NaCl
|
||||
# Erlang bindings for NaCl/libsodium
|
||||
|
||||
This library provides bindings for the NaCl cryptographic library for Erlang. Several such libraries exist, but this one is a re-write with a number of different requirements, and foci:
|
||||
This library provides bindings for the libsodium cryptographic library
|
||||
for Erlang. Originally called NaCl by Bernstein, Lange and Schwabe[0],
|
||||
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.
|
||||
|
||||
### INSTALL/Requirements:
|
||||
## INSTALL/Requirements
|
||||
|
||||
* Erlang/OTP 17.3. This library *needs* the newest dirty scheduler implementation.
|
||||
* *Requires* the libsodium library. *Note:* libsodium is not packaged in Debian/Ubuntu by default. You need to use something to handle the installation for you. E.g., `checkinstall` or `stow` are good tools for this. For other systems, consult your package manager on how to install the package. Make sure you also get "development" packages containing the header file `libsodium.h`.
|
||||
* New-ish Erlang installation. Tested back to version 22.3, but version 21 *may*
|
||||
work as well.
|
||||
* *Requires* the libsodium library, and has been tested with version
|
||||
1.0.18. Lower versions might work, or they might fail to compile,
|
||||
due to missing functionality. In particular, this means your libsodium installation
|
||||
must be fairly recent as well.
|
||||
|
||||
*Note:* If installing on systems which cuts packages into
|
||||
subpackages, make sure you also get the "-dev" package containing
|
||||
the header files necessary in order to compile software linking to
|
||||
libsodium.
|
||||
|
||||
To build the software execute:
|
||||
|
||||
make
|
||||
|
||||
make
|
||||
|
||||
or
|
||||
|
||||
rebar compile
|
||||
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. Most notably access to a proper CSPRNG random source
|
||||
* 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,
|
||||
crypto-operations runs quickly on modern CPUs, with ample security
|
||||
margins. This makes it highly useful on the server-side, where
|
||||
simultaneous concurrent load on the system means encryption can have
|
||||
a considerable overhead.
|
||||
* Is tested on Linux, FreeBSD and Illumos (Omnios)
|
||||
|
||||
This package draws heavy inspiration from "erlang-nacl" by Tony Garnock-Jones.
|
||||
This package draws heavy inspiration from "erlang-nacl" by Tony
|
||||
Garnock-Jones, and started its life with a gently nod in that
|
||||
direction. However, it is a rewrite and it alters lots of code from
|
||||
Tony's original work.
|
||||
|
||||
In addition, I would like to thank Steve Vinoski, Rickard Green, and Sverker Eriksson for providing the Dirty Scheduler API in the first place.
|
||||
In addition, I would like to thank Steve Vinoski, Rickard Green, and
|
||||
Sverker Eriksson for providing the Dirty Scheduler API in the first
|
||||
place.
|
||||
|
||||
# TODO
|
||||
## Usage
|
||||
|
||||
* Write simple correctness unit tests for the different NaCl primitives.
|
||||
In general, consult the libsodium documentation at [Libsodium documentation](https://download.libsodium.org/doc/)
|
||||
|
||||
# Versions
|
||||
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)
|
||||
|
||||
## v0.11.x
|
||||
but also note that our interface has full Edoc documentation,
|
||||
generated by executing
|
||||
|
||||
### v0.11.0
|
||||
rebar3 doc
|
||||
|
||||
* 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.
|
||||
## Hints
|
||||
|
||||
## v0.10.x
|
||||
In general, the primitives provided by NaCl are intermediate-level
|
||||
primitives. Rather than you having to select a cipher suite, it is
|
||||
selected for you, and primitives are provided at a higher level.
|
||||
However, their correct use is still needed in order to be secure:
|
||||
|
||||
### v0.10.2
|
||||
* Always make sure you obey the scheme of *nonce* values. If you ever
|
||||
reuse a nonce, and an attacker figures this out, the system will
|
||||
leak the XOR difference of messages sent with the same nonce. Given
|
||||
enough guessing, this can in turn leak the encryption stream of bits
|
||||
and every message hereafter, sent on the same keypair combination
|
||||
and reusing that nonce, will be trivially breakable.
|
||||
* Use the beforenm/afternm primitives if using the `box` public-key
|
||||
encryption scheme. Precomputing the Curve25519 operations yields
|
||||
much faster operation in practice for a stream. Consult the `bench`
|
||||
directory for benchmarks in order to see how much faster it is for
|
||||
your system. The authors Core i7-4900MQ can process roughly 32
|
||||
Kilobyte data on the stream in the time it takes to do the
|
||||
Curve25519 computations. While NaCl is *fast*, this can make it even
|
||||
faster in practice.
|
||||
* Encrypting very large blocks of data, several megabytes for
|
||||
instance, is problematic for two reasons. First, while the library
|
||||
attempts to avoid being a memory hog, you need at least a from-space
|
||||
and a to-space for the data, meaning you need at least double the
|
||||
memory for the operation. Furthermore, while such large blocks are
|
||||
executed on the dirty schedulers, they will never yield the DS for
|
||||
another piece of work. This means you end up blocking the dirty
|
||||
schedulers in turn. It is often better to build a framing scheme and
|
||||
encrypt data in smaller chunks, say 64 or 128 kilobytes at a time.
|
||||
In any case, it is important to measure. Especially for latency.
|
||||
* The library should provide correct success type specifications. This
|
||||
means you can use the dialyzer on your code and get hints for
|
||||
incorrect usage of the library.
|
||||
* Note that every "large" input to the library accepts `iodata()`
|
||||
rather than `binary()` data. The library itself will convert
|
||||
`iodata()` to binaries internally, so you don't have to do it at
|
||||
your end. It often yields simpler code since you can just build up
|
||||
an iolist of your data and shove it to the library. Key material,
|
||||
nonces and the like are generally *not* accepted as `iodata()`
|
||||
however but requires you to input binary data. This is a deliberate
|
||||
choice since most such material is not supposed to be broken up and
|
||||
constructed ever (except perhaps for the Nonce construction).
|
||||
* The `enacl:randombytes/1` function provides portable access to the
|
||||
CSPRNG of your kernel. It is an *excellent* source of CSPRNG random
|
||||
data. If you need PRNG data with a seed for testing purposes, use
|
||||
the `rand` module of Erlang. The other alternative is the `crypto`
|
||||
module, which are bindings to OpenSSL with all its blessings and/or
|
||||
curses.
|
||||
* Beware of timing attacks against your code! A typical area is string
|
||||
comparison, where the comparator function exits early. In that case,
|
||||
an attacker can time the response in order to guess at how many
|
||||
bytes where matched. This in turn enables some attacks where you use
|
||||
a foreign system as an oracle in order to learn the structure of a
|
||||
string, breaking the cryptograhic system in the process.
|
||||
|
||||
Maintenance release. Fix some usability problems with the library.
|
||||
## Versions
|
||||
|
||||
* 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)
|
||||
See CHANGELOG.md
|
||||
|
||||
### v0.10.1
|
||||
## Overview
|
||||
|
||||
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.
|
||||
The NaCl cryptographic library provides a number of different
|
||||
cryptographic primitives. In the following, we split up the different
|
||||
generic primitives and explain them briefly.
|
||||
|
||||
### v0.10.0
|
||||
*A note on Nonces:* The crypto API makes use of "cryptographic
|
||||
nonces", that is arbitrary numbers which are used only once. For these
|
||||
primitives to be secure it is important to consult the NaCl
|
||||
documentation on their choice. They are large values so generating
|
||||
them randomly ensures security, provided the random number generator
|
||||
uses a sufficiently large period. If you end up using, say, the nonce
|
||||
`7` every time in communication while using the same keys, then the
|
||||
security falls.
|
||||
|
||||
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.x
|
||||
|
||||
Ultra-late beta. Code probably works, but it requires some real-world use before it is deemed entirely stable.
|
||||
|
||||
### v0.9.0
|
||||
|
||||
Initial release.
|
||||
|
||||
# Overview
|
||||
|
||||
The NaCl cryptographic library provides a number of different cryptographic primitives. In the following, we split up the different generic primitives and explain them briefly.
|
||||
|
||||
*A note on Nonces:* The crypto API makes use of "cryptographic nonces", that is arbitrary numbers which are used only once. For these primitives to be secure it is important to consult the NaCl documentation on their choice. They are large values so generating them randomly ensures security, provided the random number generator uses a sufficiently large period. If you end up using, say, the nonce `7` every time in communication while using the same keys, then the security falls.
|
||||
|
||||
The reason you can pick the nonce values is because some uses are better off using a nonce-construction based on monotonically increasing numbers, while other uses do not. The advantage of a sequence is that it can be used to reject older messages in the stream and protect against replay attacks. So the correct use is up to the application in many cases.
|
||||
The reason you can pick the nonce values is because some uses are
|
||||
better off using a nonce-construction based on monotonically
|
||||
increasing numbers, while other uses do not. The advantage of a
|
||||
sequence is that it can be used to reject older messages in the stream
|
||||
and protect against replay attacks. So the correct use is up to the
|
||||
application in many cases.
|
||||
|
||||
## Public Key cryptography
|
||||
|
||||
This implements standard Public/Secret key cryptography. The implementation roughly consists of two major sections:
|
||||
This implements standard Public/Secret key cryptography. The
|
||||
implementation roughly consists of two major sections:
|
||||
|
||||
* *Authenticated encryption:* provides a `box` primitive which encrypts and then also authenticates a message. The reciever is only able to open the sealed box if they posses the secret key and the authentication from the sender is correct.
|
||||
* *Signatures:* allows one party to sign a message (not encrypting it) so another party can verify the message has the right origin.
|
||||
* *Authenticated encryption:* provides a `box` primitive which
|
||||
encrypts and then also authenticates a message. The reciever is only
|
||||
able to open the sealed box if they posses the secret key and the
|
||||
authentication from the sender is correct.
|
||||
* *Signatures:* allows one party to sign a message (not encrypting it)
|
||||
so another party can verify the message has the right origin.
|
||||
|
||||
## Secret key cryptography
|
||||
|
||||
This implements cryptography where there is a shared secret key between parties.
|
||||
|
||||
* *Authenticated encryption:* provides a `secret box` primitive in which we can encrypt a message with a shared key `k`. The box also authenticates the message, so a message with an invalid key will be rejected as well. This protects against the application obtaining garbage data.
|
||||
* *Encryption:* provides streams of bytes based on a Key and a Nonce. These streams can be used to `XOR` with a message to encrypt it. No authentication is provided. The API allows for the system to `XOR` the message for you while producing the stream.
|
||||
* *Authentication:* Provides an implementation of a Message Authentication Code (MAC).
|
||||
* *One Time Authentication:* Authenticate a message, but do so one-time. That is, a sender may *never* authenticate several messages under the same key. Otherwise an attacker can forge authenticators with enough time. The primitive is simpler and faster than the MAC authenticator however, so it is useful in some situations.
|
||||
* *Authenticated encryption:* provides a `secret box` primitive in
|
||||
which we can encrypt a message with a shared key `k`. The box also
|
||||
authenticates the message, so a message with an invalid key will be
|
||||
rejected as well. This protects against the application obtaining
|
||||
garbage data.
|
||||
* *Encryption:* provides streams of bytes based on a Key and a Nonce.
|
||||
These streams can be used to `XOR` with a message to encrypt it. No
|
||||
authentication is provided. The API allows for the system to `XOR`
|
||||
the message for you while producing the stream.
|
||||
* *Authentication:* Provides an implementation of a Message
|
||||
Authentication Code (MAC).
|
||||
* *One Time Authentication:* Authenticate a message, but do so
|
||||
one-time. That is, a sender may *never* authenticate several
|
||||
messages under the same key. Otherwise an attacker can forge
|
||||
authenticators with enough time. The primitive is simpler and faster
|
||||
than the MAC authenticator however, so it is useful in some
|
||||
situations.
|
||||
|
||||
## Low-level functions
|
||||
|
||||
* *Hashing:* Cryptographically secure hashing
|
||||
* *String comparison:* Implements guaranteed constant-time string comparisons to protect against timing attacks.
|
||||
* *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 vehicle. Second, cryptographic systems must be void of timing attacks. This mandates we write the code in a language where we can avoid such timing attacks, which leaves only C as a contender, more or less. The obvious way to handle this is by the use of NIF implementations, but most C code will run to its conclusion once set off for processing. This is a major problem for a system which needs to keep its latency in check. The solution taken by this library is to use the new Dirty Scheduler API of Erlang in order to provide a safe way to handle the long-running cryptographic processing. It keeps the cryptographic primitives on the dirty schedulers and thus it avoids the major problem.
|
||||
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
|
||||
vehicle. Second, cryptographic systems must be void of timing attacks.
|
||||
This mandates we write the code in a language where we can avoid such
|
||||
timing attacks, which leaves only C as a contender, more or less. The
|
||||
obvious way to handle this is by the use of NIF implementations, but
|
||||
most C code will run to its conclusion once set off for processing.
|
||||
This is a major problem for a system which needs to keep its latency
|
||||
in check. The solution taken by this library is to use the new Dirty
|
||||
Scheduler API of Erlang in order to provide a safe way to handle the
|
||||
long-running cryptographic processing. It keeps the cryptographic
|
||||
primitives on the dirty schedulers and thus it avoids the major
|
||||
problem.
|
||||
|
||||
Focus has first and foremost been on the correct use of dirty schedulers, without any regard for speed. The plan is to extend the underlying implementation, while keeping the API stable. We can precompute keys for some operations for instance, which will yield a speedup.
|
||||
Focus has first and foremost been on the correct use of dirty
|
||||
schedulers, without any regard for speed. The plan is to extend the
|
||||
underlying implementation, while keeping the API stable. We can
|
||||
precompute keys for some operations for instance, which will yield a
|
||||
speedup.
|
||||
|
||||
Also, while the standard `crypto` bindings in Erlang does a great job at providing cryptographic primitives, these are based on OpenSSL, which is known to be highly problematic in many ways. It is not as easy to use the OpenSSL library correctly as it is with these bindings. Rather than providing a low-level cipher suite, NaCl provides intermediate level primitives constructed as to protect the user against typical low-level cryptographic gotchas and problems.
|
||||
Also, while the standard `crypto` bindings in Erlang does a great job
|
||||
at providing cryptographic primitives, these are based on OpenSSL,
|
||||
which is known to be highly problematic in many ways. It is not as
|
||||
easy to use the OpenSSL library correctly as it is with these
|
||||
bindings. Rather than providing a low-level cipher suite, NaCl
|
||||
provides intermediate level primitives constructed as to protect the
|
||||
user against typical low-level cryptographic gotchas and problems.
|
||||
|
||||
## Scheduler handling
|
||||
|
||||
To avoid long running NIFs, the library switches to the use of dirty schedulers for large encryption tasks. The target is roughly set at 1/10th of the 1ms budget at 100μs. That is, we have a threshold set such that work taking more than roughly 100μs will invoke the dirty scheduler. We currently care much more about the *progress* of the system rather than the *precision*. We care that another Erlang process gets to use the core so one process is unable to monopolize the scheduler thread. On the other hand, the price that a process pays to use encryption is something we care less about. A process may get a free ride or it may get penalized more than it should if it invokes crypto-code.
|
||||
To avoid long running NIFs, the library switches to the use of dirty
|
||||
schedulers for large encryption tasks. We investigated the Dirty
|
||||
Scheduler switch overhead with DTrace on FreeBSD and found it to be
|
||||
roughly 5μs in typical cases. Thus, we target calls taking at least
|
||||
35μs is being easier to run directly on the dirty scheduler, as the
|
||||
overhead for switching is thus going to be less than 15%. This means
|
||||
very small operations are run directly on the BEAM scheduler, but as
|
||||
soon as the operation takes a little longer, the switch overhead is
|
||||
not large enough to warrant the current schedulers involvement.
|
||||
|
||||
We currently use measurements to obtain some rough figures on the reduction counts different operations take. You can run these measurements by invoking:
|
||||
In turn, some operations are *always* run on the dirty scheduler
|
||||
because they take a long time in every case. This setup is far simpler
|
||||
for most operations, unless the operation is performance sensitive and
|
||||
allows small messages.
|
||||
|
||||
enacl_timing:all().
|
||||
|
||||
The current "typical modern machine" is:
|
||||
The tests were conducted on a Core 2 Duo machine, with newer machines
|
||||
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.
|
||||
|
||||
Intel Core i7-4900QM
|
||||
|
||||
When running benchmarks, we warm the CPU a bit before conducting the benchmark. Also, the script `benchmark.sh` can be used (altered to your CPU type), to disable the powersave mode of CPUs in order to obtain realistic benchmarks. Do note nothing was done to get a realistic disable of Intel's Turbo Boost functionality and this is a one-core benchmark. The numbers given are used as an input to the reduction budget. If a task takes roughly 134μs we assume it costs `134*2` reductions.
|
||||
## Testing
|
||||
|
||||
I'm interested in machines for which the schedules end up being far off. That is, machines for which the current CPU schedule takes more than 250μs. This is especially interesting for virtual machines, and machines with ARM cores. If you are running on very slow machines, you may have to tune the reduction counts and threshold sizes to get good latency on the system.
|
||||
Every primitive has been stress-tested through the use of Erlang
|
||||
QuickCheck with both *positive* and *negative* testing. This has been
|
||||
used to check against memory leaks as well as correct invocation.
|
||||
Please report any error so we can extend the test cases to include a
|
||||
randomized test which captures the problem so we generically catch
|
||||
every problem in a given class of errors.
|
||||
|
||||
# Testing
|
||||
Positive and negative testing refers to Type I and Type II errors in
|
||||
statistical testing. This means false positives—given a *valid* input
|
||||
the function rejects it; as well as false negatives—given an *invalid*
|
||||
input the functions fails to reject that input.
|
||||
|
||||
Every primitive has been stress-tested through the use of Erlang QuickCheck with both *positive* and *negative* testing. This has been used to check against memory leaks as well as correct invocation. Please report any error so we can extend the test cases to include a randomized test which captures the problem so we generically catch every problem in a given class of errors.
|
||||
The problem however, is that while we are testing the API level, we
|
||||
can't really test the strength of the cryptographic primitives. We can
|
||||
verify their correctness by trying different standard correctness
|
||||
tests for the primitives, verifying that the output matches the
|
||||
expected one given a specific input. But there is no way we can show
|
||||
that the cryptographic primitive has the strength we want. Thus, we
|
||||
opted to mostly test the API and its invocation for stability.
|
||||
|
||||
Positive and negative testing refers to Type I and Type II errors in statistical testing. This means false positives—given a *valid* input the function rejects it; as well as false negatives—given an *invalid* input the functions fails to reject that input.
|
||||
Also, in addition to correctness, testing the system like this makes
|
||||
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.
|
||||
|
||||
The problem however, is that while we are testing the API level, we can't really test the strength of the cryptographic primitives. We can verify their correctness by trying different standard correctness tests for the primitives, verifying that the output matches the expected one given a specific input. But there is no way we can show that the cryptographic primitive has the strength we want. Thus, we opted to mostly test the API and its invocation for stability.
|
||||
|
||||
Also, in addition to correctness, testing the system like this makes 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
|
||||
|
||||
[0] Other people have worked on bits and pieces of NaCl. These are
|
||||
just the 3 main authors. Please see the page [NaCl](http://nacl.cr.yp.to)
|
||||
for the full list of authors.
|
||||
|
37
bench/enacl_nif.d
Normal file
37
bench/enacl_nif.d
Normal file
@ -0,0 +1,37 @@
|
||||
/* Dirty NIF schedule overhead */
|
||||
pid$target:beam.smp:schedule_dirty_cpu_nif:return
|
||||
{
|
||||
s = timestamp;
|
||||
}
|
||||
|
||||
pid$target:libsodium.so.*:randombytes:entry {
|
||||
e = timestamp;
|
||||
}
|
||||
|
||||
pid$target:beam.smp:execute_dirty_nif:entry
|
||||
/s != 0/
|
||||
{
|
||||
@SchedTime = lquantize(timestamp - s, 0, 10000, 250);
|
||||
s = 0;
|
||||
}
|
||||
|
||||
pid$target:beam.smp:execute_dirty_nif:return
|
||||
{
|
||||
@ExecTime = lquantize(timestamp - e, 0, 10000, 250);
|
||||
e = 0;
|
||||
r = timestamp;
|
||||
}
|
||||
|
||||
pid$target:beam.smp:dirty_nif_finalizer:entry
|
||||
/r != 0/
|
||||
{
|
||||
@ReturnTime = lquantize(timestamp - r, 0, 10000, 250);
|
||||
r = 0;
|
||||
}
|
||||
|
||||
END
|
||||
{
|
||||
printa("Scheduling overhead (nanos):%@d\n", @SchedTime);
|
||||
printa("Return overhead (nanos):%@d\n", @ReturnTime);
|
||||
printa("Exec time (nanos):%@d\n", @ExecTime);
|
||||
}
|
9
bench/funcall_enacl.d
Normal file
9
bench/funcall_enacl.d
Normal file
@ -0,0 +1,9 @@
|
||||
erlang*:::nif-entry
|
||||
{
|
||||
funcall_entry_ts[cpu, copyinstr(arg1)] = timestamp;
|
||||
}
|
||||
|
||||
erlang*:::nif-return
|
||||
{
|
||||
@time[cpu, copyinstr(arg1)] = lquantize((timestamp - funcall_entry_ts[cpu, copyinstr(arg1)] ), 0, 60000, 1000);
|
||||
}
|
150
bench/timing.erl
Normal file
150
bench/timing.erl
Normal file
@ -0,0 +1,150 @@
|
||||
-module(timing).
|
||||
-export([test/0]).
|
||||
|
||||
test() ->
|
||||
randombytes(),
|
||||
randombytes(),
|
||||
randombytes(),
|
||||
hash(),
|
||||
box_keypair(),
|
||||
box(),
|
||||
box_before_after(),
|
||||
sign_keypair(),
|
||||
sign(),
|
||||
secretbox(),
|
||||
stream(),
|
||||
auth(),
|
||||
onetime_auth(),
|
||||
scalarmult(),
|
||||
ok.
|
||||
|
||||
randombytes() ->
|
||||
randombytes(100*1000).
|
||||
|
||||
randombytes(0) -> ok;
|
||||
randombytes(N) ->
|
||||
enacl:randombytes(1024),
|
||||
randombytes(N-1).
|
||||
|
||||
hash() ->
|
||||
B = binary:copy(<<0>>, 4096),
|
||||
hash(B, 10*1000).
|
||||
|
||||
hash(_B, 0) -> ok;
|
||||
hash(B, N) ->
|
||||
enacl:hash(B),
|
||||
hash(B, N-1).
|
||||
|
||||
box_keypair() ->
|
||||
box_keypair(10*1000).
|
||||
|
||||
box_keypair(0) -> ok;
|
||||
box_keypair(N) ->
|
||||
enacl:box_keypair(),
|
||||
box_keypair(N-1).
|
||||
|
||||
box() ->
|
||||
#{ public := PK1} = enacl:box_keypair(),
|
||||
#{ secret := SK2} = enacl:box_keypair(),
|
||||
B = binary:copy(<<0>>, 1),
|
||||
Nonce = binary:copy(<<0>>, enacl:box_NONCEBYTES()()),
|
||||
box(B, Nonce, PK1, SK2, 10*1000).
|
||||
|
||||
box(_B, _Nonce, _PK1, _SK2, 0) -> ok;
|
||||
box(B, Nonce, PK1, SK2, N) ->
|
||||
enacl:box(B, Nonce, PK1, SK2),
|
||||
enacl:box_seal(B, PK1),
|
||||
box(B, Nonce, PK1, SK2, N-1).
|
||||
|
||||
box_before_after() ->
|
||||
#{ public := PK1 } = enacl:box_keypair(),
|
||||
#{ secret := SK2 } = enacl:box_keypair(),
|
||||
box_beforenm(PK1, SK2, 10*1000),
|
||||
R = enacl:box_beforenm(PK1, SK2),
|
||||
B = binary:copy(<<0>>, 8192),
|
||||
Nonce = binary:copy(<<0>>, enacl:box_NONCEBYTES()()),
|
||||
box_afternm(B, Nonce, R, 10*1000),
|
||||
ok.
|
||||
|
||||
box_beforenm(_PK, _SK, 0) -> ok;
|
||||
box_beforenm(PK, SK, N) ->
|
||||
enacl:box_beforenm(PK, SK),
|
||||
box_beforenm(PK, SK, N-1).
|
||||
|
||||
box_afternm(_Msg, _Nonce, _Key, 0) -> ok;
|
||||
box_afternm(Msg, Nonce, Key, N) ->
|
||||
enacl:box_afternm(Msg, Nonce, Key),
|
||||
box_afternm(Msg, Nonce, Key, N-1).
|
||||
|
||||
sign_keypair() ->
|
||||
sign_keypair(10*1000).
|
||||
|
||||
sign_keypair(0) -> ok;
|
||||
sign_keypair(N) ->
|
||||
enacl:sign_keypair(),
|
||||
#{ public := PK, secret := SK} = enacl:crypto_sign_ed25519_keypair(),
|
||||
enacl:crypto_sign_ed25519_public_to_curve25519(PK),
|
||||
enacl:crypto_sign_ed25519_secret_to_curve25519(SK),
|
||||
sign_keypair(N-1).
|
||||
|
||||
sign() ->
|
||||
Msg = binary:copy(<<0>>, 1024),
|
||||
#{ secret := SK } = enacl:sign_keypair(),
|
||||
sign(Msg, SK, 10*1000).
|
||||
|
||||
sign(_Msg, _SK, 0) -> ok;
|
||||
sign(Msg, SK, N) ->
|
||||
enacl:sign(Msg, SK),
|
||||
enacl:sign_detached(Msg, SK),
|
||||
sign(Msg, SK, N-1).
|
||||
|
||||
secretbox() ->
|
||||
Msg = binary:copy(<<0>>, 8192),
|
||||
Nonce = binary:copy(<<0>>, enacl:secretbox_NONCEBYTES()()),
|
||||
Key = binary:copy(<<0>>, enacl:secretbox_KEYBYTES()),
|
||||
secretbox(Msg, Nonce, Key, 10*1000).
|
||||
|
||||
secretbox(_Msg, _Nonce, _Key, 0) -> ok;
|
||||
secretbox(Msg, Nonce, Key, N) ->
|
||||
enacl:secretbox(Msg, Nonce, Key),
|
||||
secretbox(Msg, Nonce, Key, N-1).
|
||||
|
||||
|
||||
stream() ->
|
||||
stream(16384, binary:copy(<<0>>, enacl:stream_NONCEBYTES()), binary:copy(<<0>>, enacl:stream_KEYBYTES()), 10*1000).
|
||||
|
||||
stream(_L, _Nonce, _K, 0) -> ok;
|
||||
stream(L, Nonce, K, N) ->
|
||||
enacl:stream(L, Nonce, K),
|
||||
stream(L, Nonce, K, N-1).
|
||||
|
||||
auth() ->
|
||||
Msg = binary:copy(<<0>>, 4096),
|
||||
Key = binary:copy(<<0>>, enacl:auth_KEYBYTES()),
|
||||
auth(Msg, Key, 10*1000).
|
||||
|
||||
auth(_Msg, _Key, 0) -> ok;
|
||||
auth(Msg, Key, N) ->
|
||||
enacl:auth(Msg, Key),
|
||||
auth(Msg, Key, N-1).
|
||||
|
||||
onetime_auth() ->
|
||||
Msg = binary:copy(<<0>>, 16384),
|
||||
Key = binary:copy(<<0>>, enacl:onetime_auth_KEYBYTES()),
|
||||
onetime_auth(Msg, Key, 10*1000).
|
||||
|
||||
onetime_auth(_Msg, _Key, 0) -> ok;
|
||||
onetime_auth(Msg, Key, N) ->
|
||||
enacl:onetime_auth(Msg, Key),
|
||||
onetime_auth(Msg, Key, N-1).
|
||||
|
||||
scalarmult() ->
|
||||
Secret = binary:copy(<<0>>, 32),
|
||||
BasePoint = binary:copy(<<1>>, 32),
|
||||
scalarmult(Secret, BasePoint, 10*1000).
|
||||
|
||||
scalarmult(_S, _B, 0) -> ok;
|
||||
scalarmult(S, B, N) ->
|
||||
enacl:curve25519_scalarmult(S, B),
|
||||
scalarmult(S, B, N-1).
|
||||
|
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
|
1186
c_src/enacl_nif.c
1186
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
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
29
eqc_test/enacl_ext_eqc.erl
Normal file
29
eqc_test/enacl_ext_eqc.erl
Normal file
@ -0,0 +1,29 @@
|
||||
-module(enacl_ext_eqc).
|
||||
|
||||
-include_lib("eqc/include/eqc.hrl").
|
||||
-compile({parse_transform, eqc_parallelize}).
|
||||
-compile([export_all, nowarn_export_all]).
|
||||
|
||||
public_keypair() ->
|
||||
?LET(#{ public := PK, secret := SK}, enacl_ext:curve25519_keypair(),
|
||||
{PK, SK}).
|
||||
|
||||
prop_public_key() ->
|
||||
?FORALL({PK, SK}, public_keypair(),
|
||||
begin
|
||||
equals(PK, enacl_ext:curve25519_public_key(SK))
|
||||
end).
|
||||
|
||||
prop_shared_secret() ->
|
||||
?FORALL([{PK1, SK1}, {PK2, SK2}],
|
||||
[public_keypair(), public_keypair()],
|
||||
begin
|
||||
Alice = enacl_ext:curve25519_shared(SK1, PK2),
|
||||
Bob = enacl_ext:curve25519_shared(SK2, PK1),
|
||||
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))).
|
||||
|
0
priv/.dummy
Normal file
0
priv/.dummy
Normal file
39
rebar.config
39
rebar.config
@ -1,8 +1,39 @@
|
||||
{erl_opts, [debug_info]}.
|
||||
|
||||
{port_env, [
|
||||
{"CFLAGS", "$CFLAGS $LIBSODIUM_CFLAGS"},
|
||||
{"LDFLAGS", "$LDFLAGS $LIBSODIUM_LDFLAGS -lsodium"}
|
||||
{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_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"}
|
||||
]}.
|
||||
|
1
rebar.lock
Normal file
1
rebar.lock
Normal file
@ -0,0 +1 @@
|
||||
[].
|
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.11.0"},
|
||||
{registered, []},
|
||||
{applications, [kernel, stdlib]},
|
||||
{env, []}
|
||||
]}.
|
||||
{application,enacl,
|
||||
[{description,"Erlang libsodium (NaCl) bindings"},
|
||||
{vsn,"1.2.1"},
|
||||
{registered,[]},
|
||||
{applications,[kernel,stdlib]},
|
||||
{env,[]},
|
||||
{maintainers,["Jesper Louis Andersen"]},
|
||||
{licenses,["MIT","ISC"]},
|
||||
{links,[{"Github","https://github.com/jlouis/enacl"}]}]}.
|
||||
|
1612
src/enacl.erl
1612
src/enacl.erl
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,20 @@
|
||||
%%% @doc module enacl_ext implements various enacl extensions.
|
||||
%%% <p>None of the extensions listed here are part of the official NaCl library.
|
||||
%%% Things may be removed without further notice if it suddenly ends up being
|
||||
%%% Functions may be removed without further notice if it suddenly ends up being
|
||||
%%% better to do something differently than the solution given here.
|
||||
%%% </p>
|
||||
-module(enacl_ext).
|
||||
|
||||
-export([
|
||||
scramble_block_16/2
|
||||
]).
|
||||
scramble_block_16/2
|
||||
]).
|
||||
|
||||
%% Curve25519
|
||||
-export([
|
||||
curve25519_keypair/0,
|
||||
curve25519_public_key/1,
|
||||
curve25519_shared/2
|
||||
]).
|
||||
|
||||
%% @doc scramble_block_16/2 scrambles (encrypt) a block under a given key
|
||||
%% The rules are that the block is 16 bytes and the key is 32 bytes. The block
|
||||
@ -23,3 +30,29 @@
|
||||
-spec scramble_block_16(binary(), binary()) -> binary().
|
||||
scramble_block_16(Block, Key) ->
|
||||
enacl_nif:scramble_block_16(Block, Key).
|
||||
|
||||
%% Curve 25519 Crypto
|
||||
%% ------------------
|
||||
%% @doc curve25519_keypair/0 creates a new Public/Secret keypair.
|
||||
%%
|
||||
%% Generates and returns a new key pair for the Curve 25519 encryption scheme. The return value is a
|
||||
%% map in order to avoid using the public key as a secret key and vice versa.
|
||||
%% @end
|
||||
-spec curve25519_keypair() -> #{ atom() => binary() }.
|
||||
curve25519_keypair() ->
|
||||
<<B0:8/integer, B1:30/binary, B2:8/integer>> = enacl:randombytes(32),
|
||||
SK = <<(B0 band 248), B1/binary, (64 bor (B2 band 127))>>,
|
||||
PK = curve25519_public_key(SK),
|
||||
#{ public => PK, secret => SK }.
|
||||
|
||||
%% @doc curve25519_public_key/1 creates a public key from a given SecretKey.
|
||||
%% @end
|
||||
-spec curve25519_public_key(SecretKey :: binary()) -> binary().
|
||||
curve25519_public_key(SecretKey) ->
|
||||
enacl:curve25519_scalarmult(SecretKey, <<9, 0:248>>).
|
||||
|
||||
%% @doc curve25519_shared/2 creates a new shared secret from a given SecretKey and PublicKey.
|
||||
%% @end.
|
||||
-spec curve25519_shared(SecretKey :: binary(), PublicKey :: binary()) -> binary().
|
||||
curve25519_shared(SecretKey, PublicKey) ->
|
||||
enacl:curve25519_scalarmult(SecretKey, PublicKey).
|
||||
|
@ -5,102 +5,259 @@
|
||||
|
||||
%% Public key auth
|
||||
-export([
|
||||
crypto_box_BOXZEROBYTES/0,
|
||||
crypto_box_NONCEBYTES/0,
|
||||
crypto_box_PUBLICKEYBYTES/0,
|
||||
crypto_box_SECRETKEYBYTES/0,
|
||||
crypto_box_ZEROBYTES/0,
|
||||
crypto_box_BEFORENMBYTES/0,
|
||||
crypto_box_BOXZEROBYTES/0,
|
||||
crypto_box_NONCEBYTES/0,
|
||||
crypto_box_PUBLICKEYBYTES/0,
|
||||
crypto_box_SECRETKEYBYTES/0,
|
||||
crypto_box_ZEROBYTES/0,
|
||||
crypto_box_BEFORENMBYTES/0,
|
||||
|
||||
crypto_box/4,
|
||||
crypto_box_b/4,
|
||||
crypto_box_keypair/0,
|
||||
crypto_box_open/4,
|
||||
crypto_box_open_b/4,
|
||||
crypto_box_keypair/0,
|
||||
|
||||
crypto_box_beforenm/2,
|
||||
crypto_box_afternm/3,
|
||||
crypto_box_afternm_b/3,
|
||||
crypto_box_open_afternm/3,
|
||||
crypto_box_open_afternm_b/3,
|
||||
crypto_box/4,
|
||||
crypto_box_open/4,
|
||||
|
||||
crypto_sign_PUBLICKEYBYTES/0,
|
||||
crypto_sign_SECRETKEYBYTES/0,
|
||||
crypto_box_beforenm/2,
|
||||
crypto_box_afternm/3,
|
||||
crypto_box_afternm_b/3,
|
||||
crypto_box_open_afternm/3,
|
||||
crypto_box_open_afternm_b/3,
|
||||
|
||||
crypto_sign/2,
|
||||
crypto_sign_b/2,
|
||||
crypto_sign_keypair/0,
|
||||
crypto_sign_open/2,
|
||||
crypto_sign_open_b/2
|
||||
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,
|
||||
|
||||
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
|
||||
]).
|
||||
|
||||
%% Secret key crypto
|
||||
-export([
|
||||
crypto_secretbox_BOXZEROBYTES/0,
|
||||
crypto_secretbox_KEYBYTES/0,
|
||||
crypto_secretbox_NONCEBYTES/0,
|
||||
crypto_secretbox_ZEROBYTES/0,
|
||||
crypto_secretbox_BOXZEROBYTES/0,
|
||||
crypto_secretbox_KEYBYTES/0,
|
||||
crypto_secretbox_NONCEBYTES/0,
|
||||
crypto_secretbox_ZEROBYTES/0,
|
||||
|
||||
crypto_secretbox/3,
|
||||
crypto_secretbox_b/3,
|
||||
crypto_secretbox_open/3,
|
||||
crypto_secretbox_open_b/3,
|
||||
crypto_secretbox/3,
|
||||
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_KEYBYTES/0,
|
||||
crypto_stream_NONCEBYTES/0,
|
||||
crypto_stream_chacha20_KEYBYTES/0,
|
||||
crypto_stream_chacha20_NONCEBYTES/0,
|
||||
|
||||
crypto_stream/3,
|
||||
crypto_stream_b/3,
|
||||
crypto_stream_xor/3,
|
||||
crypto_stream_xor_b/3,
|
||||
crypto_stream_chacha20/3,
|
||||
crypto_stream_chacha20_b/3,
|
||||
crypto_stream_chacha20_xor/3,
|
||||
crypto_stream_chacha20_xor_b/3,
|
||||
|
||||
crypto_auth_BYTES/0,
|
||||
crypto_auth_KEYBYTES/0,
|
||||
crypto_stream_KEYBYTES/0,
|
||||
crypto_stream_NONCEBYTES/0,
|
||||
|
||||
crypto_auth/2,
|
||||
crypto_auth_b/2,
|
||||
crypto_auth_verify/3,
|
||||
crypto_auth_verify_b/3,
|
||||
crypto_stream/3,
|
||||
crypto_stream_b/3,
|
||||
crypto_stream_xor/3,
|
||||
crypto_stream_xor_b/3,
|
||||
|
||||
crypto_onetimeauth_BYTES/0,
|
||||
crypto_onetimeauth_KEYBYTES/0,
|
||||
crypto_aead_chacha20poly1305_ietf_encrypt/4,
|
||||
crypto_aead_chacha20poly1305_ietf_decrypt/4,
|
||||
crypto_aead_chacha20poly1305_ietf_KEYBYTES/0,
|
||||
crypto_aead_chacha20poly1305_ietf_NPUBBYTES/0,
|
||||
crypto_aead_chacha20poly1305_ietf_ABYTES/0,
|
||||
crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX/0,
|
||||
|
||||
crypto_onetimeauth/2,
|
||||
crypto_onetimeauth_b/2,
|
||||
crypto_onetimeauth_verify/3,
|
||||
crypto_onetimeauth_verify_b/3
|
||||
]).
|
||||
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,
|
||||
|
||||
crypto_auth/2,
|
||||
crypto_auth_b/2,
|
||||
crypto_auth_verify/3,
|
||||
crypto_auth_verify_b/3,
|
||||
|
||||
crypto_shorthash_BYTES/0,
|
||||
crypto_shorthash_KEYBYTES/0,
|
||||
|
||||
crypto_shorthash/2,
|
||||
|
||||
crypto_onetimeauth_BYTES/0,
|
||||
crypto_onetimeauth_KEYBYTES/0,
|
||||
|
||||
crypto_onetimeauth/2,
|
||||
crypto_onetimeauth_b/2,
|
||||
crypto_onetimeauth_verify/3,
|
||||
crypto_onetimeauth_verify_b/3
|
||||
]).
|
||||
|
||||
%% Curve25519
|
||||
-export([
|
||||
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,
|
||||
crypto_sign_ed25519_SECRETKEYBYTES/0
|
||||
]).
|
||||
|
||||
%% Key exchange
|
||||
-export([
|
||||
crypto_kx_keypair/0,
|
||||
crypto_kx_server_session_keys/3,
|
||||
crypto_kx_client_session_keys/3,
|
||||
crypto_kx_SESSIONKEYBYTES/0,
|
||||
crypto_kx_PUBLICKEYBYTES/0,
|
||||
crypto_kx_SECRETKEYBYTES/0
|
||||
]).
|
||||
|
||||
%% Miscellaneous helper functions
|
||||
-export([
|
||||
crypto_hash/1,
|
||||
crypto_hash_b/1,
|
||||
crypto_verify_16/2,
|
||||
crypto_verify_32/2
|
||||
]).
|
||||
crypto_hash/1,
|
||||
crypto_hash_b/1,
|
||||
crypto_verify_16/2,
|
||||
crypto_verify_32/2,
|
||||
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_b/1
|
||||
]).
|
||||
randombytes/1,
|
||||
randombytes_uint32/0,
|
||||
randombytes_uniform/1
|
||||
]).
|
||||
|
||||
%% Undocumented features :>
|
||||
-export([
|
||||
scramble_block_16/2
|
||||
]).
|
||||
scramble_block_16/2
|
||||
]).
|
||||
|
||||
-on_load(init/0).
|
||||
|
||||
init() ->
|
||||
SoName = filename:join(
|
||||
case code:priv_dir(enacl) of
|
||||
{error, bad_name} ->
|
||||
filename:join(filename:dirname(filename:dirname(code:which(?MODULE))), "priv");
|
||||
Dir ->
|
||||
Dir
|
||||
end, atom_to_list(?MODULE)),
|
||||
erlang:load_nif(SoName, 0).
|
||||
Dir = case code:priv_dir(enacl) of
|
||||
{error, bad_name} ->
|
||||
filename:join(
|
||||
filename:dirname(
|
||||
filename:dirname(
|
||||
code:which(?MODULE))), "priv");
|
||||
D -> D
|
||||
end,
|
||||
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).
|
||||
@ -111,9 +268,7 @@ crypto_box_BEFORENMBYTES() -> erlang:nif_error(nif_not_loaded).
|
||||
|
||||
crypto_box_keypair() -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_box(_PaddedMsg, _Nonce, _PK, _SK) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_box_b(_PaddedMsg, _Nonce, _PK, _SK) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_box_open(_CipherText, _Nonce, _PK, _SK) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_box_open_b(_CipherText, _Nonce, _PK, _SK) -> erlang:nif_error(nif_not_loaded).
|
||||
|
||||
crypto_box_beforenm(_PK, _SK) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_box_afternm(_M, _Nonce, _K) -> erlang:nif_error(nif_not_loaded).
|
||||
@ -123,12 +278,25 @@ 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_b(_M, _SK) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_sign_open(_SignedMessage, _PK) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_sign_open_b(_SignedMessage, _PK) -> erlang:nif_error(nif_not_loaded).
|
||||
|
||||
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).
|
||||
|
||||
crypto_secretbox_NONCEBYTES() -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_secretbox_ZEROBYTES() -> erlang:nif_error(nif_not_loaded).
|
||||
@ -139,6 +307,17 @@ 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).
|
||||
crypto_stream_chacha20(_Bytes, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_stream_chacha20_b(_Bytes, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_stream_chacha20_xor(_M, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_stream_chacha20_xor_b(_M, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
|
||||
crypto_stream_KEYBYTES() -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_stream_NONCEBYTES() -> erlang:nif_error(nif_not_loaded).
|
||||
@ -147,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).
|
||||
@ -154,6 +347,10 @@ crypto_auth_b(_Msg, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_auth_verify(_Authenticator, _Msg, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_auth_verify_b(_Authenticator, _Msg, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
|
||||
crypto_shorthash_BYTES() -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_shorthash_KEYBYTES() -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_shorthash(_Msg, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
|
||||
crypto_onetimeauth_BYTES() -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_onetimeauth_KEYBYTES() -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_onetimeauth(_Msg, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
@ -161,12 +358,31 @@ crypto_onetimeauth_b(_Msg, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_onetimeauth_verify(_Authenticator, _Msg, _Key) -> erlang:nif_error(nif_not_loaded).
|
||||
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).
|
||||
crypto_sign_ed25519_SECRETKEYBYTES() -> erlang:nif_error(nif_not_loaded).
|
||||
|
||||
crypto_hash(Input) when is_binary(Input) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_hash_b(Input) when is_binary(Input) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_verify_16(_X, _Y) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_verify_32(_X, _Y) -> erlang:nif_error(nif_not_loaded).
|
||||
sodium_memzero(Input) when is_binary(Input) -> erlang:nif_error(nif_not_loaded).
|
||||
|
||||
crypto_kx_keypair() -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_kx_server_session_keys(_ServerPk,_ServerSk,_ClientPk) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_kx_client_session_keys(_ClientPk,_ClientSk,_ServerPk) -> erlang:nif_error(nif_not_loaded).
|
||||
crypto_kx_SESSIONKEYBYTES() -> erlang:nif_error(nif_not_loaded).
|
||||
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_b(_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).
|
||||
|
@ -1,232 +0,0 @@
|
||||
%%% @doc module enacl_timing provides helpers for timing enacl toward your installation
|
||||
%%% @end
|
||||
-module(enacl_timing).
|
||||
|
||||
-export([all/0]).
|
||||
|
||||
all() ->
|
||||
[time_hashing(),
|
||||
time_box(),
|
||||
time_sign(),
|
||||
time_secretbox(),
|
||||
time_stream(),
|
||||
time_auth(),
|
||||
time_onetimeauth(),
|
||||
time_precomputed()].
|
||||
|
||||
-define(ROUNDS, 300).
|
||||
|
||||
%% ONETIMEAUTH
|
||||
%% ------------
|
||||
|
||||
time_onetimeauth() ->
|
||||
Sz = 1024 * 128,
|
||||
M = binary:copy(<<0>>, Sz),
|
||||
K = <<"secretsecretsecretsecretsecret32">>,
|
||||
T = timed(fun() -> onetime_auth(M, K, ?ROUNDS) end) / ?ROUNDS,
|
||||
A = enacl:onetime_auth(M, K),
|
||||
T2 = timed(fun() -> onetime_auth_verify(A, M, K, ?ROUNDS) end) / ?ROUNDS,
|
||||
true = enacl:onetime_auth_verify(A, M, K),
|
||||
[
|
||||
#{ size => Sz, time => T, operation => onetime_auth },
|
||||
#{ size => Sz, time => T2, operation => onetime_auth_verify }
|
||||
].
|
||||
|
||||
onetime_auth(_M, _K, 0) -> ok;
|
||||
onetime_auth(M, K, N) ->
|
||||
enacl_nif:crypto_onetimeauth_b(M, K),
|
||||
onetime_auth(M, K, N-1).
|
||||
|
||||
onetime_auth_verify(_A, _M, _K, 0) -> ok;
|
||||
onetime_auth_verify(A, M, K, N) ->
|
||||
enacl_nif:crypto_onetimeauth_verify_b(A, M, K),
|
||||
onetime_auth_verify(A, M, K, N-1).
|
||||
|
||||
%% AUTH
|
||||
%% -----------
|
||||
|
||||
time_auth() ->
|
||||
Sz = 1024 * 32,
|
||||
M = binary:copy(<<0>>, Sz),
|
||||
K = <<"secretsecretsecretsecretsecret32">>,
|
||||
T = timed(fun() -> auth(M, K, ?ROUNDS) end) / ?ROUNDS,
|
||||
A = enacl:auth(M, K),
|
||||
T2 = timed(fun() -> auth_verify(A, M, K, ?ROUNDS) end) / ?ROUNDS,
|
||||
true = enacl:auth_verify(A, M, K),
|
||||
[
|
||||
#{ size => Sz, time => T, operation => auth },
|
||||
#{ size => Sz, time => T2, operation => auth_verify }
|
||||
].
|
||||
|
||||
auth(_M, _K, 0) -> ok;
|
||||
auth(M, K, N) ->
|
||||
enacl_nif:crypto_auth_b(M, K),
|
||||
auth(M, K, N-1).
|
||||
|
||||
auth_verify(_A, _M, _K, 0) -> ok;
|
||||
auth_verify(A, M, K, N) ->
|
||||
enacl_nif:crypto_auth_verify_b(A, M, K),
|
||||
auth_verify(A, M, K, N-1).
|
||||
|
||||
%% STREAM
|
||||
%% -----------
|
||||
|
||||
time_stream() ->
|
||||
Sz = 1024 * 128,
|
||||
K = <<"secretsecretsecretsecretsecret32">>,
|
||||
Nonce = <<0:192>>,
|
||||
T = timed(fun () -> stream(Sz, Nonce, K, ?ROUNDS) end) / ?ROUNDS,
|
||||
M = binary:copy(<<0>>, Sz),
|
||||
T2 = timed(fun () -> stream_xor(M, Nonce, K, ?ROUNDS) end) / ?ROUNDS,
|
||||
[
|
||||
#{ size => Sz, time => T, operation => stream },
|
||||
#{ size => Sz, time => T2, operation => stream_xor }
|
||||
].
|
||||
|
||||
stream(_Sz, _Nonce, _K, 0) -> ok;
|
||||
stream(Sz, Nonce, K, N) ->
|
||||
enacl_nif:crypto_stream_b(Sz, Nonce, K),
|
||||
stream(Sz, Nonce, K, N-1).
|
||||
|
||||
stream_xor(_M, _Nonce, _K, 0) -> ok;
|
||||
stream_xor(M, Nonce, K, N) ->
|
||||
enacl_nif:crypto_stream_xor_b(M, Nonce, K),
|
||||
stream_xor(M, Nonce, K, N-1).
|
||||
|
||||
%% SECRETBOX
|
||||
%% ----------
|
||||
|
||||
time_secretbox() ->
|
||||
Sz = 1024 * 64,
|
||||
M = binary:copy(<<0>>, Sz),
|
||||
K = <<"secretsecretsecretsecretsecret32">>,
|
||||
Nonce = binary:copy(<<0:192>>),
|
||||
T = timed(fun() -> secretbox(M, Nonce, K, ?ROUNDS) end) / ?ROUNDS,
|
||||
CT = enacl:secretbox(M, Nonce, K),
|
||||
T2 = timed(fun() -> secretbox_open(CT, Nonce, K, ?ROUNDS) end) / ?ROUNDS,
|
||||
{ok, M} = enacl:secretbox_open(CT, Nonce, K),
|
||||
[
|
||||
#{ size => Sz, time => T, operation => secretbox },
|
||||
#{ size => Sz, time => T2, operation => secretbox_open }
|
||||
].
|
||||
|
||||
secretbox(_M, _Nonce, _K, 0) -> ok;
|
||||
secretbox(M, Nonce, K, N) ->
|
||||
enacl_nif:crypto_secretbox_b(M, Nonce, K),
|
||||
secretbox(M, Nonce, K, N-1).
|
||||
|
||||
secretbox_open(_M, _Nonce, _K, 0) -> ok;
|
||||
secretbox_open(M, Nonce, K, N) ->
|
||||
enacl_nif:crypto_secretbox_open_b(M, Nonce, K),
|
||||
secretbox_open(M, Nonce, K, N-1).
|
||||
|
||||
%% SIGN
|
||||
%% ---------
|
||||
time_sign() ->
|
||||
Sz = 1024 * 16,
|
||||
M = binary:copy(<<0>>, Sz),
|
||||
#{ public := PK, secret := SK } = enacl:sign_keypair(),
|
||||
T = timed(fun() -> sign(M, SK, ?ROUNDS) end) / ?ROUNDS,
|
||||
SM = enacl:sign(M, SK),
|
||||
T2 = timed(fun() -> sign_open(SM, PK, ?ROUNDS) end) / ?ROUNDS,
|
||||
[
|
||||
#{ size => Sz, time => T, operation => sign },
|
||||
#{ size => Sz, time => T2, operation => sign_open }
|
||||
].
|
||||
|
||||
sign(_M, _SK, 0) -> ok;
|
||||
sign(M, SK, N) ->
|
||||
enacl_nif:crypto_sign_b(M, SK),
|
||||
sign(M, SK, N-1).
|
||||
|
||||
sign_open(_SM, _PK, 0) -> ok;
|
||||
sign_open(SM, PK, N) ->
|
||||
enacl_nif:crypto_sign_open_b(SM, PK),
|
||||
sign_open(SM, PK, N-1).
|
||||
|
||||
%% BOX
|
||||
%% --------
|
||||
time_box() ->
|
||||
Sz = 1024 * 32,
|
||||
ZB = binary:copy(<<0>>, enacl_nif:crypto_box_ZEROBYTES()),
|
||||
BZB = binary:copy(<<0>>, enacl_nif:crypto_box_BOXZEROBYTES()),
|
||||
Bin = binary:copy(<<0>>, Sz),
|
||||
Nonce = binary:copy(<<0>>, enacl_nif:crypto_box_NONCEBYTES()),
|
||||
#{ public := PK1, secret := SK1 } = enacl:box_keypair(),
|
||||
#{ public := PK2, secret := SK2 } = enacl:box_keypair(),
|
||||
T = timed(fun() -> box([ZB, Bin], Nonce, PK1, SK2, ?ROUNDS) end) / ?ROUNDS,
|
||||
Boxed = enacl:box([ZB, Bin], Nonce, PK1, SK2),
|
||||
T2 = timed(fun() -> box_open([BZB, Boxed], Nonce, PK2, SK1, ?ROUNDS) end) / ?ROUNDS,
|
||||
[
|
||||
#{ size => Sz, time => T, operation => box},
|
||||
#{ size => Sz, time => T2, operation => box_open}
|
||||
].
|
||||
|
||||
box_open(_Bin, _Nonce, _PK, _SK, 0) -> ok;
|
||||
box_open(Bin, Nonce, PK, SK, N) ->
|
||||
enacl_nif:crypto_box_open_b(Bin, Nonce, PK, SK),
|
||||
box_open(Bin, Nonce, PK, SK, N-1).
|
||||
|
||||
box(_Bin, _Nonce, _PK, _SK, 0) -> ok;
|
||||
box(Bin, Nonce, PK, SK, N) ->
|
||||
enacl_nif:crypto_box_b(Bin, Nonce, PK, SK),
|
||||
box(Bin, Nonce, PK, SK, N-1).
|
||||
|
||||
%% PRECOMPUTED
|
||||
%% -------------------
|
||||
|
||||
time_precomputed() ->
|
||||
Sz = 1024 * 64,
|
||||
Bin = binary:copy(<<0>>, Sz),
|
||||
ZB = binary:copy(<<0>>, enacl_nif:crypto_box_ZEROBYTES()),
|
||||
BZB = binary:copy(<<0>>, enacl_nif:crypto_box_BOXZEROBYTES()),
|
||||
Nonce = binary:copy(<<0>>, enacl_nif:crypto_box_NONCEBYTES()),
|
||||
#{ public := PK1, secret := SK1 } = enacl:box_keypair(),
|
||||
#{ public := PK2, secret := SK2 } = enacl:box_keypair(),
|
||||
T = timed(fun() -> beforenm(PK1, SK2, ?ROUNDS) end) / ?ROUNDS,
|
||||
K = enacl_nif:crypto_box_beforenm(PK1, SK2),
|
||||
K = enacl_nif:crypto_box_beforenm(PK2, SK1),
|
||||
T2 = timed(fun() -> afternm([ZB, Bin], Nonce, K, ?ROUNDS) end) / ?ROUNDS,
|
||||
Ciphered = enacl_nif:crypto_box_afternm_b([ZB, Bin], Nonce, K),
|
||||
Bin = enacl_nif:crypto_box_open_afternm_b([BZB, Ciphered], Nonce, K),
|
||||
T3 = timed(fun() -> afternm_open([BZB, Ciphered], Nonce, K, ?ROUNDS) end) / ?ROUNDS,
|
||||
[
|
||||
#{ size => 'n/a', time => T, operation => box_beforenm },
|
||||
#{ size => Sz, time => T2, operation => box_afternm },
|
||||
#{ size => Sz, time => T3, operation => box_open_afternm }
|
||||
].
|
||||
|
||||
afternm(_M, _Nonce, _K, 0) -> ok;
|
||||
afternm(M, Nonce, K, N) ->
|
||||
enacl_nif:crypto_box_afternm_b(M, Nonce, K),
|
||||
afternm(M, Nonce, K, N-1).
|
||||
|
||||
afternm_open(_C, _Nonce, _K, 0) -> ok;
|
||||
afternm_open(C, Nonce, K, N) ->
|
||||
enacl_nif:crypto_box_open_afternm_b(C, Nonce, K),
|
||||
afternm_open(C, Nonce, K, N-1).
|
||||
|
||||
beforenm(_PK, _SK, 0) -> ok;
|
||||
beforenm(PK, SK, N) ->
|
||||
enacl_nif:crypto_box_beforenm(PK, SK),
|
||||
beforenm(PK, SK, N-1).
|
||||
|
||||
%% HASHING
|
||||
%% ----------------
|
||||
time_hashing() ->
|
||||
Sz = 1024 * 32,
|
||||
Bin = binary:copy(<<0>>, Sz),
|
||||
T = timed(fun() -> hash(Bin, ?ROUNDS) end) / ?ROUNDS,
|
||||
#{ size => Sz, time => T, operation => hash}.
|
||||
|
||||
hash(_Bin, 0) -> ok;
|
||||
hash(Bin, N) ->
|
||||
enacl_nif:crypto_hash_b(Bin),
|
||||
hash(Bin, N-1).
|
||||
|
||||
%% Helpers
|
||||
timed(Fun) ->
|
||||
Fun(), % warmup
|
||||
{T, _} = timer:tc(Fun),
|
||||
T.
|
||||
|
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