jlouis / enacl

Erlang bindings for NaCl / libsodium
MIT License
197 stars 59 forks source link
cryptography enryption erlang erlang-bindings erlang-quickcheck libsodium

Erlang bindings for NaCl/libsodium

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

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

or

rebar compile

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

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.

Usage

In general, consult the libsodium documentation at Libsodium documentation

The original NaCl documentation is nowadays largely superceded by the libsodium documentation, but it is still worth a visit NaCl website

but also note that our interface has full Edoc documentation, generated by executing

rebar3 doc

Hints

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:

Versions

See CHANGELOG.md

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.

Public Key cryptography

This implements standard Public/Secret key cryptography. The implementation roughly consists of two major sections:

Secret key cryptography

This implements cryptography where there is a shared secret key between parties.

Low-level functions

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.

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.

Scheduler handling

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.

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.

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.

Testing

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.

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.

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 for the full list of authors.