github.com/fero46/sodium.cr

Crystal wrapper for the libsodium crypto API

0 stars
1 dependent
License: MIT

Nothing has been indexed for 1.0.1 yet. The tag is recorded, its shard.yml has not been read, so the manifest and dependency list below are empty because they are unknown rather than because they are absent.

Installation

# Add this to your shard.yml
dependencies:
  sodium:
    github: fero46/sodium.cr
    version: ~> 1.0.1

Then run:

shards install

shard.yml

No shard.yml has been indexed for 1.0.1. You can read it on the repository.

Dependencies

Unknown: the shard.yml for this version has not been read yet.

README

This README is the one indexed from the repository at its latest ref, not from the tag for this version.

sodium.cr

Build Status GitHub release Docs

Crystal bindings for the libsodium API

Goals

  • Provide the most commonly used libsodium API's.
  • Provide an easy to use API based on reviewing most other libsodium bindings.
  • 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.

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 SecretBoxI don't know much about crypto.
Sodium::CryptoBox::SecretKey .boxI want to encrypt + authenticate data using public key encryption.
Sodium::CryptoBox::SecretKey .encryptI want anonymously send encrypted data. (No signatures)
Sodium::Sign::SecretKeyI want to sign or verify messages. (No encryption)
Sodium::SecretBoxI have a shared key and want to encrypt + authenticate data.
Sodium::Cipher::Aead::XChaCha20Poly1305IetfI have a shared key and want to encrypt + authenticate data and authentication additional plaintext data.
Sodium::Cipher::SecretStreamI have a shared key and want encrypt + authenticate streamed data.
Sodium::Digest::Blake2bI want to hash data fast and securely.
Sodium::Digest::SipHashI want to hash data really fast and less securely. (Not implemented yet)
Sodium::Password::HashI want to hash a password and store it.
Sodium::Password::KeyI want to derive a key from a password.
Sodium::KdfI have a high quality master key and want to make subkeys.
Sodium::Cipher::ChalsaWhat goes with guacamole?
Everything elseI 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 →

1416642561024409616384655362621441048576
8K0.542s2.114s
32K0.513s2.069s
128K0.530s2.121s
512K0.566s2.237s
2048K0.567s2.290s
8192K0.670s2.542s
32768K0.684s2.777s
131072K0.805s3.106s
524288K0.504s1.135s3.661s
2097152K2.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

  • Originally created by Andrew Hamon
  • Forked by Didactic Drunk 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.

Contributors