sodium.cr

Crystal CI GitHub release GitHub commits since latest release (by date) for a branch Docs

Crystal bindings for the libsodium API

Goals

Features

☑ Indicate specs are compared against test vectors from another source.

Several features in libsodium are already provided by Crystal:

What should I use for my application?

| Class | | | --- | --- | | Only use CryptoBox::SecretKey Sign::SecretKey Aead::XChaCha20Poly1305Ietf SecretBox | I don't know much about crypto. | | Sodium::CryptoBox::SecretKey .box | I want to encrypt + authenticate data using public key encryption. | | Sodium::CryptoBox::SecretKey .encrypt | I want anonymously send encrypted data. (No signatures) | | Sodium::Sign::SecretKey | I want to sign or verify messages. (No encryption) | | Sodium::Cipher::Aead::XChaCha20Poly1305Ietf (new applications) Sodium::SecretBox (compatibility with older applications) | I have a shared key and want to encrypt + authenticate data. | | Sodium::Cipher::Aead::XChaCha20Poly1305Ietf | I have a shared key and want to encrypt + authenticate data and authenticate additional plaintext data. | | Sodium::Cipher::SecretStream | I have a shared key and want encrypt + authenticate streamed data. | | Sodium::Digest::Blake2b | I want to hash data fast and securely. | | Sodium::Digest::SipHash | I want to hash data really fast and less securely. (Not implemented yet) | | Sodium::Password::Hash | I want to hash a password and store it. | | Sodium::Password::Key | I want to derive a key from a password. | | Sodium::Kdf | I have a high quality master key and want to make subkeys. | | Sodium::Cipher::Chalsa | What goes with guacamole? | | Everything else | I want to design my own crypto protocol and probably do it wrong. |

Installation

Optionally Install libsodium. 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:

dependencies:
  sodium:
    github: didactic-drunk/sodium.cr

Usage

See examples for help on using these classes in a complete application.

The specs provide the best examples of how to use or misuse individual classes.


### CryptoBox authenticated easy encryption
```crystal
require "sodium"

data = "Hello World!"

# Alice is the sender
alice = Sodium::CryptoBox::SecretKey.new

# Bob is the recipient
bob = Sodium::CryptoBox::SecretKey.new

# Precompute a shared secret between alice and bob.
box = alice.box bob.public_key

# Encrypt a message for Bob using his public key, signing it with Alice's
# secret key
encrypted, nonce = box.encrypt data

# Precompute within a block.  The shared secret is wiped when the block exits.
bob.box alice.public_key do |box|
  # Decrypt the message using Bob's secret key, and verify its signature against
  # Alice's public key
  decrypted = box.decrypt encrypted, nonce: nonce

  String.new(decrypted) # => "Hello World!"
end

Unauthenticated public key encryption

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!"

Public key signing

message = "Hello World!"

secret_key = Sodium::Sign::SecretKey.new

# Sign the message
signature = secret_key.sign_detached message

# Send secret_key.public_key to the recipient

# On the recipient
public_key = Sodium::Sign::PublicKey.new key_bytes

# raises Sodium::Error::VerificationFailed on failure.
public_key.verify_detached message, signature

Secret Key Encryption

box = Sodium::SecretBox.new

message = "foobar"
encrypted, nonce = box.encrypt message

# On the other side.
box = Sodium::SecretKey.new key
message = box.decrypt encrypted, nonce: nonce

Blake2b

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
data = "data".to_slice

# output_size, key, salt, and personal are optional.
digest = Sodium::Digest::Blake2b.new out_size, key: key, salt: salt, personal: personal
digest.update data
output = d.hexfinal

digest.reset # Reuse existing object to hash again.
digest.update data
output = d.hexfinal

Key derivation

kdf = Sodium::Kdf.new

# 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

Password based keys

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

Password Hashing

pwhash = Sodium::Password::Hash.new

pwhash.mem = Sodium::Password::MEMLIMIT_MIN
pwhash.ops = Sodium::Password::OPSLIMIT_MIN

pass = "1234"
hash = pwhash.create pass
pwhash.verify hash, pass

Use examples/pwhash_selector.cr to help choose ops/mem limits.

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 |

Contributing

  1. Fork it ( https://github.com/didactic-drunk/sodium.cr/fork )
  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

Project History

Contributors