0c4f5b3ad9
echo to stderr |
||
---|---|---|
.github/workflows | ||
benchmarks | ||
build | ||
examples | ||
scripts/git | ||
spec | ||
src | ||
.editorconfig | ||
.gitignore | ||
.travis.yml | ||
LICENSE | ||
README.md | ||
shard.yml | ||
THREAD_SAFETY.md |
sodium.cr
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
- Public-Key Cryptography
- Secret-Key Cryptography
- Secret Box
- Secret Stream
- AEAD
- AES256-GCM (Requires hardware acceleration)
- ☑ XChaCha20-Poly1305-IETF
- ChaCha20-Poly1305-IETF
- ChaCha20-Poly1305
- Combined and detached mode
- Hashing
- Password Hashing
- Other
- Advanced
- Stream Ciphers
- XSalsa20
- Salsa20
- XChaCha20
- ChaCha20 Ietf
- ChaCha20
- Easy to use methods available for use as a CSPRNG that are faster and safer than Crystal's. See
benchmarks/rand.out
.
- One time auth
- Padding
- Stream Ciphers
- Library features
- Faster builds by requiring what you need (
require "sodium/secret_box"
) - Nonce reuse detection.
- All SecretKey's held in libsodium guarded memory.
- No heap allocations after #initialize when possible.
- Fast. Benchmarks available in
benchmarks
. - Most classes are safe to share between threads.
- Tested with real crystal threads and will continue to work when crystal officially supports threading.
- Controlled memory wiping (by calling
.close
)
- Faster builds by requiring what you need (
☑ Indicate specs are compared against test vectors from another source.
Several features in libsodium are already provided by Crystal:
- Random (Use Random::Secure)
- SHA-2 (Use OpenSSL::Digest)
- HMAC SHA-2 (Use OpenSSL::HMAC)
- Hex conversion (Use String#hexbytes)
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
- Fork it ( https://github.com/didactic-drunk/sodium.cr/fork )
- Install a formatting check git hook (ln -sf ../../scripts/git/pre-commit .git/hooks)
- Create your feature branch (git checkout -b my-new-feature)
- Commit your changes (git commit -am 'Add some feature')
- Push to the branch (git push origin my-new-feature)
- 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
- andrewhamon Andrew Hamon - creator, former maintainer
- dorkrawk Dave Schwantes - contributor
- didactic-drunk - current maintainer