2019-06-29 01:17:09 +02:00
# sodium.cr
[![Build Status ](https://travis-ci.org/didactic-drunk/sodium.cr.svg?branch=master )](https://travis-ci.org/didactic-drunk/sodium.cr)
2019-08-07 01:29:47 +02:00
[![GitHub release ](https://img.shields.io/github/release/didactic-drunk/sodium.cr.svg )](https://github.com/didactic-drunk/sodium.cr/releases)
2019-06-29 01:17:09 +02:00
[![Docs ](https://img.shields.io/badge/docs-available-brightgreen.svg )](https://didactic-drunk.github.io/sodium.cr/)
2017-07-12 05:13:52 +02:00
2019-06-29 03:29:33 +02:00
Crystal bindings for the [libsodium API ](https://libsodium.gitbook.io/doc/ )
## Goals
* Provide the most commonly used libsodium API's.
2019-06-30 02:21:00 +02:00
* Provide an easy to use API based on reviewing most other [libsodium bindings ](https://libsodium.gitbook.io/doc/bindings_for_other_languages ).
2019-06-29 03:29:33 +02:00
* Test for compatibility against other libsodium bindings to ensure interoperability.
* Always provide a stream interface to handle arbitrarily sized data when one is available.
* Drop in replacement classes compatible with OpenSSL::{Digest,Cipher} when possible.
* Use the newest system packaged libsodium or download the most recent stable version without manual configuration.
2017-07-12 05:13:52 +02:00
2019-06-27 06:10:42 +02:00
## Features
2019-06-28 22:26:31 +02:00
- [Public-Key Cryptography ](https://libsodium.gitbook.io/doc/public-key_cryptography )
2019-08-06 03:45:31 +02:00
- [x] ☑ [Crypto Box Easy ](https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption )
2019-06-30 04:20:30 +02:00
- [x] [Sealed Box ](https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes )
2019-06-28 22:26:31 +02:00
- [ ] [Combined Signatures ](https://libsodium.gitbook.io/doc/public-key_cryptography/public-key_signatures )
2019-08-06 11:20:47 +02:00
- [x] ☑ [Detached Signatures ](https://libsodium.gitbook.io/doc/public-key_cryptography/public-key_signatures )
2019-08-10 12:58:30 +02:00
- [ ] [Pre-hashed Signatures ](https://libsodium.gitbook.io/doc/public-key_cryptography/public-key_signatures )
2019-06-28 01:19:11 +02:00
- [Secret-Key Cryptography ](https://libsodium.gitbook.io/doc/secret-key_cryptography )
- Secret Box
2019-08-06 03:45:31 +02:00
- [x] ☑ [Combined mode ](https://libsodium.gitbook.io/doc/secret-key_cryptography/authenticated_encryption )
2019-06-28 22:26:31 +02:00
- [ ] [Detached mode ](https://libsodium.gitbook.io/doc/secret-key_cryptography/authenticated_encryption )
2019-07-09 08:39:00 +02:00
- [x] [Secret Stream ](https://libsodium.gitbook.io/doc/secret-key_cryptography/secretstream )
2019-06-28 22:26:31 +02:00
- [AEAD ](https://libsodium.gitbook.io/doc/secret-key_cryptography/aead )
2019-06-28 22:44:33 +02:00
- [ ] AES256-GCM (Requires hardware acceleration)
2019-09-14 13:22:05 +02:00
- [x] ☑ [XChaCha20-Poly1305-IETF ](https://libsodium.gitbook.io/doc/secret-key_cryptography/aead/chacha20-poly1305/xchacha20-poly1305_construction )
2019-09-13 11:04:49 +02:00
- [ ] [ChaCha20-Poly1305-IETF ](https://libsodium.gitbook.io/doc/secret-key_cryptography/aead/chacha20-poly1305/ietf_chacha20-poly1305_construction )
- [ ] [ChaCha20-Poly1305 ](https://libsodium.gitbook.io/doc/secret-key_cryptography/aead/chacha20-poly1305 )
2019-09-13 12:15:00 +02:00
- [x] Combined and detached mode
2019-06-28 22:26:31 +02:00
- [Hashing ](https://libsodium.gitbook.io/doc/hashing )
2019-06-28 01:19:11 +02:00
- [x] ☑ [Blake2b ](https://libsodium.gitbook.io/doc/hashing/generic_hashing )
2019-07-01 15:24:26 +02:00
- [x] Complete libsodium implementation including `key` , `salt` , `personal` and fully selectable output sizes.
2019-06-28 01:19:11 +02:00
- [ ] [SipHash ](https://libsodium.gitbook.io/doc/hashing/short-input_hashing )
- [Password Hashing ](https://libsodium.gitbook.io/doc/password_hashing )
2019-07-09 08:39:00 +02:00
- [x] ☑ [Argon2 ](https://libsodium.gitbook.io/doc/password_hashing/the_argon2i_function ) (Use for new applications)
2019-06-28 22:26:31 +02:00
- [ ] [Scrypt ](https://libsodium.gitbook.io/doc/advanced/scrypt ) (For compatibility with older applications)
2019-06-27 06:10:42 +02:00
- Other
2019-06-28 01:19:11 +02:00
- [x] [Key Derivation ](https://libsodium.gitbook.io/doc/key_derivation )
- [ ] [Key Exchange ](https://libsodium.gitbook.io/doc/key_exchange )
2019-06-30 04:31:41 +02:00
- [Advanced ](https://libsodium.gitbook.io/doc/advanced )
2019-06-28 22:26:31 +02:00
- [Stream Ciphers ](https://libsodium.gitbook.io/doc/advanced/stream_ciphers )
2019-06-28 01:19:11 +02:00
- [x] XSalsa20
- [x] Salsa20
- [x] XChaCha20
- [x] ChaCha20 Ietf
- [x] ChaCha20
2019-09-14 13:30:45 +02:00
- [x] Easy to use methods available for use as a CSPRNG that are faster and safer than Crystal's. See `benchmarks/rand.out` .
2019-06-28 22:26:31 +02:00
- [ ] [One time auth ](https://libsodium.gitbook.io/doc/advanced/poly1305 )
2019-06-28 01:19:11 +02:00
- [ ] Padding
2019-07-01 15:24:26 +02:00
- Library features
2019-07-04 02:56:02 +02:00
- [x] Faster builds by requiring what you need (`require "sodium/secret_box"`)
2019-08-10 12:58:30 +02:00
- [x] Nonce reuse detection.
2019-07-04 02:56:02 +02:00
- [x] All SecretKey's held in libsodium guarded memory.
2019-09-14 13:30:45 +02:00
- [x] No heap allocations after #initialize when possible.
- [x] Fast. Benchmarks available in `benchmarks` .
2019-09-17 16:51:05 +02:00
- [x] [Most classes are safe to share between threads. ](THREAD_SAFETY.md )
- [x] Tested with real crystal threads and will continue to work when crystal officially supports threading.
- [x] Most classes are safe to share between threads. Even
2019-07-04 02:56:02 +02:00
- [ ] Controlled memory wiping (by calling `.close` )
2019-06-27 06:10:42 +02:00
2019-06-28 01:19:11 +02:00
☑ Indicate specs are compared against test vectors from another source.
Several features in libsodium are already provided by Crystal:
2019-06-27 22:52:09 +02:00
* Random (Use [Random::Secure ](https://crystal-lang.org/api/latest/Random/Secure.html ))
2019-06-27 06:10:42 +02:00
* SHA-2 (Use [OpenSSL::Digest ](https://crystal-lang.org/api/latest/OpenSSL/Digest.html ))
* HMAC SHA-2 (Use [OpenSSL::HMAC ](https://crystal-lang.org/api/latest/OpenSSL/HMAC.html ))
2019-08-06 03:45:31 +02:00
* Hex conversion (Use [String#hexbytes ](https://crystal-lang.org/api/latest/String.html#hexbytes%3ABytes-instance-method ))
2019-06-27 06:10:42 +02:00
2019-06-28 01:19:11 +02:00
2019-06-28 22:26:31 +02:00
## What should I use for my application?
| Class | |
| --- | --- |
2019-06-30 04:20:30 +02:00
| Only use `CryptoBox::SecretKey` `Sign::SecretKey` `SecretBox` | I don't know much about crypto. |
| [`Sodium::CryptoBox::SecretKey` ](https://didactic-drunk.github.io/sodium.cr/Sodium/CryptoBox/SecretKey.html ) .box | I want to encrypt + authenticate data using public key encryption. |
2019-07-09 09:42:56 +02:00
| [`Sodium::CryptoBox::SecretKey` ](https://didactic-drunk.github.io/sodium.cr/Sodium/CryptoBox/PublicKey.html ) .encrypt | I want anonymously send encrypted data. (No signatures) |
| [`Sodium::Sign::SecretKey` ](https://didactic-drunk.github.io/sodium.cr/Sodium/Sign/SecretKey.html ) | I want to sign or verify messages. (No encryption) |
2019-06-29 01:17:09 +02:00
| [`Sodium::SecretBox` ](https://didactic-drunk.github.io/sodium.cr/Sodium/SecretBox.html ) | I have a shared key and want to encrypt + authenticate data. |
2019-09-14 13:30:45 +02:00
| [`Sodium::Cipher::Aead::XChaCha20Poly1305Ietf` ](https://didactic-drunk.github.io/sodium.cr/Sodium/Cipher/Aead/XChaCha20Poly1305Ietf.html ) | I have a shared key and want to encrypt + authenticate data and authentication additional plaintext data. |
| [`Sodium::Cipher::SecretStream` ](https://didactic-drunk.github.io/sodium.cr/Sodium/Cipher/SecretStream/XChaCha20Poly1305.html ) | I have a shared key and want encrypt + authenticate streamed data. |
2019-06-29 01:17:09 +02:00
| [`Sodium::Digest::Blake2b` ](https://didactic-drunk.github.io/sodium.cr/Sodium/Digest::Blake2b.html ) | I want to hash data fast and securely. |
| `Sodium::Digest::SipHash` | I want to hash data really fast and less securely. (Not implemented yet) |
2019-09-17 13:20:38 +02:00
| [`Sodium::Password::Hash` ](https://didactic-drunk.github.io/sodium.cr/Sodium/Password/Hash.html ) | I want to hash a password and store it. |
| [`Sodium::Password::Key` ](https://didactic-drunk.github.io/sodium.cr/Sodium/Password/Key.html ) | I want to derive a key from a password. |
2019-07-09 09:42:56 +02:00
| [`Sodium::Kdf` ](https://didactic-drunk.github.io/sodium.cr/Sodium/Kdf.html ) | I have a high quality master key and want to make subkeys. |
2019-06-29 01:17:09 +02:00
| [`Sodium::Cipher::Chalsa` ](https://didactic-drunk.github.io/sodium.cr/Sodium/Cipher/Chalsa.html ) | What goes with guacamole? |
2019-06-28 22:26:31 +02:00
| Everything else | I want to design my own crypto protocol and probably do it wrong. |
2019-06-29 03:29:33 +02:00
## Installation
**[Optionally Install libsodium.](https://download.libsodium.org/doc/installation/)**
A recent version of libsodium is automatically downloaded and compiled if you don't install your own version.
Add this to your application's `shard.yml` :
```yaml
dependencies:
sodium.cr:
github: didactic-drunk/sodium.cr
```
2017-07-12 05:13:52 +02:00
## Usage
2019-07-04 02:56:02 +02:00
See `examples` for help on using these classes in a complete application.
2019-06-28 22:44:33 +02:00
2019-07-04 02:56:02 +02:00
The `specs` provide the best examples of how to use or misuse individual classes.
2019-06-29 03:29:33 +02:00
You may call `.close` on any object that retains keying material to wipe it's key(s) earlier.
Objects with a `.close` method also respond to `Class.open` and wipe when the block returns.
```crystal
2019-07-04 02:56:02 +02:00
# TODO
2019-06-29 03:29:33 +02:00
Sodium::CryptoBox::SecretKey.open(sec_key, pub_key) do |secret_key|
... Do crypto operations ...
end
# sec_key is wiped
# public keys aren't wiped.
```
2019-06-30 04:20:30 +02:00
### CryptoBox authenticated easy encryption
2017-07-12 05:13:52 +02:00
```crystal
2019-06-29 01:17:09 +02:00
require "sodium"
2017-07-12 05:13:52 +02:00
data = "Hello World!"
# Alice is the sender
2019-06-29 01:17:09 +02:00
alice = Sodium::CryptoBox::SecretKey.new
2017-07-12 05:13:52 +02:00
# Bob is the recipient
2019-06-29 01:17:09 +02:00
bob = Sodium::CryptoBox::SecretKey.new
2019-06-28 13:32:16 +02:00
# Precompute a shared secret between alice and bob.
2019-06-29 21:44:47 +02:00
box = alice.box bob.public_key
2017-07-12 05:13:52 +02:00
# Encrypt a message for Bob using his public key, signing it with Alice's
# secret key
2019-08-31 11:47:34 +02:00
encrypted, nonce = box.encrypt data
2017-07-12 05:13:52 +02:00
2019-06-28 13:32:16 +02:00
# Precompute within a block. The shared secret is wiped when the block exits.
2019-06-29 21:44:47 +02:00
bob.box alice.public_key do |box|
2019-06-28 13:32:16 +02:00
# Decrypt the message using Bob's secret key, and verify its signature against
# Alice's public key
2019-08-31 11:47:34 +02:00
decrypted = box.decrypt encrypted, nonce: nonce
2017-07-12 05:13:52 +02:00
2019-06-28 13:32:16 +02:00
String.new(decrypted) # => "Hello World!"
end
2019-06-19 10:46:42 +02:00
```
2018-02-12 08:18:45 +01:00
2019-06-30 04:20:30 +02:00
### Unauthenticated public key encryption
```crystal
data = "Hello World!"
# Bob is the recipient
bob = Sodium::CryptoBox::SecretKey.new
# Encrypt a message for Bob using his public key
encrypted = bob.public_key.encrypt data
# Decrypt the message using Bob's secret key
decrypted = bob.decrypt encrypted
String.new(decrypted) # => "Hello World!"
```
2019-06-27 06:10:42 +02:00
### Public key signing
2019-06-19 10:46:42 +02:00
```crystal
2018-02-12 08:18:45 +01:00
message = "Hello World!"
2019-06-29 01:17:09 +02:00
secret_key = Sodium::Sign::SecretKey.new
2018-02-12 08:18:45 +01:00
# Sign the message
2019-06-28 12:30:33 +02:00
signature = secret_key.sign_detached message
2018-02-12 08:18:45 +01:00
2019-06-28 12:30:33 +02:00
# Send secret_key.public_key to the recipient
2019-06-30 04:20:30 +02:00
# On the recipient
2019-06-29 01:17:09 +02:00
public_key = Sodium::Sign::PublicKey.new key_bytes
2019-06-28 12:30:33 +02:00
2019-06-29 01:17:09 +02:00
# raises Sodium::Error::VerificationFailed on failure.
2019-06-28 12:30:33 +02:00
public_key.verify_detached message, signature
2017-07-12 05:13:52 +02:00
```
2019-06-27 06:10:42 +02:00
### Secret Key Encryption
2019-06-19 10:46:42 +02:00
```crystal
2019-06-29 01:17:09 +02:00
key = Sodium::SecretKey.new
2019-06-19 10:46:42 +02:00
message = "foobar"
2019-08-06 12:30:16 +02:00
encrypted, nonce = key.encrypt message
2019-06-19 10:46:42 +02:00
# On the other side.
2019-06-29 01:17:09 +02:00
key = Sodium::SecretKey.new key
2019-08-06 12:30:16 +02:00
message = key.decrypt encrypted, nonce
2019-06-19 10:46:42 +02:00
```
2019-06-27 06:10:42 +02:00
### Blake2b
2019-06-19 10:46:42 +02:00
```crystal
2019-06-29 01:17:09 +02:00
key = Bytes.new Sodium::Digest::Blake2B::KEY_SIZE
salt = Bytes.new Sodium::Digest::Blake2B::SALT_SIZE
personal = Bytes.new Sodium::Digest::Blake2B::PERSONAL_SIZE
out_size = 64 # bytes between Sodium::Digest::Blake2B::OUT_SIZE_MIN and Sodium::Digest::Blake2B::OUT_SIZE_MAX
2019-06-19 10:46:42 +02:00
data = "data".to_slice
# output_size, key, salt, and personal are optional.
2019-06-29 01:17:09 +02:00
digest = Sodium::Digest::Blake2b.new out_size, key: key, salt: salt, personal: personal
2019-06-19 10:46:42 +02:00
digest.update data
output = d.hexdigest
digest.reset # Reuse existing object to hash again.
digest.update data
output = d.hexdigest
```
2019-06-27 06:10:42 +02:00
### Key derivation
2019-06-19 10:46:42 +02:00
```crystal
2019-06-29 01:17:09 +02:00
kdf = Sodium::Kdf.new
2019-05-28 23:31:31 +02:00
2019-06-28 01:52:45 +02:00
# kdf.derive(8_byte_context, subkey_id, subkey_size)
subkey1 = kdf.derive "context1", 0, 16
subkey2 = kdf.derive "context1", 1, 16
subkey3 = kdf.derive "context2", 0, 32
subkey4 = kdf.derive "context2", 1, 64
2019-06-19 10:46:42 +02:00
```
2019-09-17 13:20:38 +02:00
### Password based keys
```crystal
pwcreate = Sodium::Password::Key::Create.new
# Take approximately 1 second to derive a key.
pwcreate.tcost = 1.0
pass = "1234"
key, params = pwcreate.create_key pass
# Store `params` or `params.to_h` for later.
# Derive the same key from the stored params.
pwkey = Sodium::Password::Key.from_params params.to_h
key = pekey.derive_key pass
```
2019-06-27 06:10:42 +02:00
### Password Hashing
2019-06-19 10:46:42 +02:00
```crystal
2019-09-17 13:20:38 +02:00
pwhash = Sodium::Password::Hash.new
2019-06-19 10:46:42 +02:00
2019-09-17 13:20:38 +02:00
pwhash.mem = Sodium::Password::MEMLIMIT_MIN
pwhash.ops = Sodium::Password::OPSLIMIT_MIN
2019-06-19 10:46:42 +02:00
pass = "1234"
2019-09-17 13:20:38 +02:00
hash = pwhash.create pass
2019-06-19 10:46:42 +02:00
pwhash.verify hash, pass
```
2019-05-28 23:31:31 +02:00
2019-06-25 21:40:58 +02:00
Use `examples/pwhash_selector.cr` to help choose ops/mem limits.
2019-06-27 18:34:23 +02:00
Example output:
Ops limit →
| | 1 | 4 | 16 | 64 | 256 | 1024 | 4096 | 16384 | 65536 | 262144 | 1048576 |
| -------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- |
| 8K | | | | | | | | | | 0.542s | 2.114s |
| 32K | | | | | | | | | 0.513s | 2.069s |
| 128K | | | | | | | | 0.530s | 2.121s |
| 512K | | | | | | | 0.566s | 2.237s |
| 2048K | | | | | | 0.567s | 2.290s |
| 8192K | | | | | 0.670s | 2.542s |
| 32768K | | | | 0.684s | 2.777s |
| 131072K | | | 0.805s | 3.106s |
| 524288K | 0.504s | 1.135s | 3.661s |
| 2097152K | 2.119s |
| Memory |
2019-09-17 13:20:38 +02:00
2017-07-12 05:13:52 +02:00
## Contributing
2019-06-29 01:17:09 +02:00
1. Fork it ( https://github.com/didactic-drunk/sodium.cr/fork )
2019-06-28 02:20:02 +02:00
2. **Install a formatting check git hook (ln -sf ../../scripts/git/pre-commit .git/hooks)**
3. Create your feature branch (git checkout -b my-new-feature)
4. Commit your changes (git commit -am 'Add some feature')
5. Push to the branch (git push origin my-new-feature)
6. Create a new Pull Request
2017-07-12 05:13:52 +02:00
2019-06-29 03:29:33 +02:00
## Project History
* Originally created by [Andrew Hamon ](https://github.com/andrewhamons/cox )
* Forked by [Didactic Drunk ](https://github.com/didactic-drunk/cox ) for lack of updates in the original project.
* Complaints about the name being too controversial. Project name changed from "cox" to a more libsodium related name of "salty seaman".
* ~50% complete libsodium API.
* More complaints about the name. Dead hooker jokes added.
* None of the original API is left.
* More complaints threatening a boycott. Told them "Go ahead, I own Coca Cola and Water".
* Account unsuspended.
* Unrelated to the boycott the project name changed to "libsodium" because sodium happens to be a tasty byproduct of the two earlier names.
* Account unsuspended.
* Dead hooker jokes (mostly) removed.
2017-07-12 05:13:52 +02:00
## Contributors
2019-06-25 22:19:59 +02:00
- [andrewhamon ](https://github.com/andrewhamon ) Andrew Hamon - creator, former maintainer
2018-02-12 08:18:45 +01:00
- [dorkrawk ](https://github.com/dorkrawk ) Dave Schwantes - contributor
2019-06-25 22:19:59 +02:00
- [didactic-drunk ](https://github.com/didactic-drunk ) - current maintainer