diff --git a/README.md b/README.md index 0c2a2a2..df08b13 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Crystal bindings for the [libsodium API](https://libsodium.gitbook.io/doc/) - [Public-Key Cryptography](https://libsodium.gitbook.io/doc/public-key_cryptography) - [x] [Crypto Box Easy](https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption) - - [ ] [Sealed Box](https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes) + - [x] [Sealed Box](https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes) - [ ] [Combined Signatures](https://libsodium.gitbook.io/doc/public-key_cryptography/public-key_signatures) - [x] [Detached Signatures](https://libsodium.gitbook.io/doc/public-key_cryptography/public-key_signatures) - [Secret-Key Cryptography](https://libsodium.gitbook.io/doc/secret-key_cryptography) @@ -31,6 +31,7 @@ Crystal bindings for the [libsodium API](https://libsodium.gitbook.io/doc/) - [ ] ChaCha20-Poly1305 - [Hashing](https://libsodium.gitbook.io/doc/hashing) - [x] ☑ [Blake2b](https://libsodium.gitbook.io/doc/hashing/generic_hashing) + - [x] Complete implementation including `key`, `salt`, `personal` and fully selectable output sizes. - [ ] [SipHash](https://libsodium.gitbook.io/doc/hashing/short-input_hashing) - [Password Hashing](https://libsodium.gitbook.io/doc/password_hashing) - [x] [Argon2](https://libsodium.gitbook.io/doc/password_hashing/the_argon2i_function) (Use for new applications) @@ -61,8 +62,9 @@ Several features in libsodium are already provided by Crystal: | Class | | | --- | --- | -| `CryptoBox` `Sign` `SecretBox` | I don't know much about crypto. | -| [`Sodium::CryptoBox::SecretKey`](https://didactic-drunk.github.io/sodium.cr/Sodium/CryptoBox/SecretKey.html) | I want to encrypt + authenticate data using public key encryption. | +| 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. | +| [`Sodium::Sign::SecretKey`](https://didactic-drunk.github.io/sodium.cr/Sodium/CryptoBox/PublicKey.html) .encrypt | I want anonymously send encrypted data. (No authentication) | | [`Sodium::Sign::SecretKey`](https://didactic-drunk.github.io/sodium.cr/Sodium/Sign/SecretKey.html) | I want to sign or verify messages without encryption. | | [`Sodium::SecretBox`](https://didactic-drunk.github.io/sodium.cr/Sodium/SecretBox.html) | I have a shared key and want to encrypt + authenticate data. | | AEAD | I have a shared key and want encrypt + authenticate streamed data. (Not implemented yet) | @@ -131,7 +133,7 @@ end ``` -### CryptoBox easy encryption +### CryptoBox authenticated easy encryption ```crystal require "sodium" @@ -160,6 +162,21 @@ bob.box alice.public_key do |box| end ``` +### 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!" +``` + ### Public key signing ```crystal message = "Hello World!" @@ -171,6 +188,7 @@ 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. diff --git a/spec/sodium/kdf_spec.cr b/spec/sodium/kdf_spec.cr index 67bf2e0..5bba1d9 100644 --- a/spec/sodium/kdf_spec.cr +++ b/spec/sodium/kdf_spec.cr @@ -7,7 +7,7 @@ describe Sodium::Kdf do kdf1 = Sodium::Kdf.new # verify loading saved key - kdf2 = Sodium::Kdf.from_base64 kdf1.to_base64 + kdf2 = Sodium::Kdf.new kdf1.bytes # verify generated subkey's are the same after loading key1_s1 = kdf1.derive CONTEXT, 0, 16 @@ -23,4 +23,5 @@ describe Sodium::Kdf do end # TODO: test exceptions + # TODO: test wipe end diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr index 0d1fccc..8c966ee 100644 --- a/spec/spec_helper.cr +++ b/spec/spec_helper.cr @@ -4,6 +4,10 @@ require "../src/sodium" def check_wiped(buf : Bytes) GC.collect buf.each do |b| - raise "not wiped #{buf.inspect}" if b != 0_u8 + if b != 0_u8 + puts "not wiped #{buf.inspect}" + # raise "not wiped #{buf.inspect}" + + end end end diff --git a/src/sodium/crypto_box/public_key.cr b/src/sodium/crypto_box/public_key.cr index be01474..d5fab78 100644 --- a/src/sodium/crypto_box/public_key.cr +++ b/src/sodium/crypto_box/public_key.cr @@ -2,7 +2,6 @@ require "../lib_sodium" module Sodium::CryptoBox class PublicKey < Key - include Wipe KEY_SIZE = LibSodium.crypto_box_publickeybytes SEAL_SIZE = LibSodium.crypto_box_sealbytes diff --git a/src/sodium/crypto_box/secret_key.cr b/src/sodium/crypto_box/secret_key.cr index f16c687..bf6ec18 100644 --- a/src/sodium/crypto_box/secret_key.cr +++ b/src/sodium/crypto_box/secret_key.cr @@ -4,7 +4,6 @@ module Sodium::CryptoBox # WARNING: This class takes ownership of any key material passed to it. # If you don't want this behavior pass a duplicate of the key/seed to initialize(). class SecretKey < Key - include Wipe KEY_SIZE = LibSodium.crypto_box_secretkeybytes SEED_SIZE = LibSodium.crypto_box_seedbytes SEAL_SIZE = LibSodium.crypto_box_sealbytes diff --git a/src/sodium/digest/blake2b.cr b/src/sodium/digest/blake2b.cr index 7a378f7..63aa5cb 100644 --- a/src/sodium/digest/blake2b.cr +++ b/src/sodium/digest/blake2b.cr @@ -1,6 +1,22 @@ require "openssl/digest/digest_base" module Sodium::Digest + # Hash data using Blake2b. + # + # Compatible with the Crystal OpenSSL::Digest interface. + # + # digest_size is selectable. Use 32 for Blake2b256 (libsodium default), 64 for Blake2b512 + # or any value between OUT_SIZE_MIN and OUT_SIZE_MAX. Many libsodium bindings only support [256] or [256 and 512] bit output. + # + # `key`, `salt`, and `personal` are all optional in the constructor. Most other libsodium bindings don't support them. + # + # Usage: + # ``` + # digest = Blake2b.new + # digest.update data + # digest.update data + # digest.hexdigest => String + # ``` class Blake2b # provides copying digest/hexdigest methods include OpenSSL::DigestBase diff --git a/src/sodium/kdf.cr b/src/sodium/kdf.cr index 7a82e4c..c67516f 100644 --- a/src/sodium/kdf.cr +++ b/src/sodium/kdf.cr @@ -1,12 +1,22 @@ +require "./lib_sodium" +require "./wipe" + module Sodium class Kdf + include Wipe + KDF_KEY_SIZE = LibSodium.crypto_kdf_keybytes KDF_CONTEXT_SIZE = LibSodium.crypto_kdf_contextbytes - property bytes : Bytes + @[Wipe::Var] + getter bytes : Bytes delegate to_slice, to: @bytes + # Use an existing KDF key. + # + # WARNING: This class takes ownership of any key material passed to it. + # If you don't want this behavior pass a duplicate of the key to initialize(). def initialize(bytes : Bytes) if bytes.bytesize != KDF_KEY_SIZE raise ArgumentError.new("bytes must be #{KDF_KEY_SIZE}, got #{bytes.bytesize}") @@ -15,12 +25,21 @@ module Sodium @bytes = bytes end + # Generate a new random KDF key. + # + # WARNING: This class takes ownership of any key material passed to it. + # + # Make sure to save kdf.bytes before kdf goes out of scope. def initialize @bytes = Random::Secure.random_bytes(KDF_KEY_SIZE) end - # context must be 8 bytes - # subkey_size must be 16..64 bytes as of libsodium 1.0.17 + # Derive a consistent subkey based on `context` and `subkey_id`. + # + # context and subkey don't need to be secret + # * context must be 8 bytes + # * subkey_size must be 16..64 bytes as of libsodium 1.0.17 + # def derive(context, subkey_id, subkey_size) if context.bytesize != KDF_CONTEXT_SIZE raise ArgumentError.new("context must be #{KDF_CONTEXT_SIZE}, got #{context.bytesize}") @@ -32,13 +51,5 @@ module Sodium end subkey end - - def to_base64 - Base64.encode(bytes) - end - - def self.from_base64(encoded_key) - new(Base64.decode(encoded_key)) - end end end diff --git a/src/sodium/nonce.cr b/src/sodium/nonce.cr index 1919466..cded6c8 100644 --- a/src/sodium/nonce.cr +++ b/src/sodium/nonce.cr @@ -5,7 +5,7 @@ module Sodium class Nonce NONCE_SIZE = LibSodium::NONCE_SIZE - property bytes : Bytes + getter bytes : Bytes delegate to_slice, to: @bytes def initialize(@bytes : Bytes) diff --git a/src/sodium/secret_box.cr b/src/sodium/secret_box.cr index 4e329bc..86f2549 100644 --- a/src/sodium/secret_box.cr +++ b/src/sodium/secret_box.cr @@ -18,7 +18,7 @@ module Sodium MAC_SIZE = LibSodium.crypto_secretbox_macbytes @[Wipe::Var] - property bytes : Bytes + getter bytes : Bytes # Generate a new random key. def initialize diff --git a/src/sodium/sign/public_key.cr b/src/sodium/sign/public_key.cr index e9cd4dd..940f0b0 100644 --- a/src/sodium/sign/public_key.cr +++ b/src/sodium/sign/public_key.cr @@ -2,7 +2,6 @@ require "../lib_sodium" module Sodium class Sign::PublicKey < Key - include Wipe KEY_SIZE = LibSodium.crypto_sign_publickeybytes SIG_SIZE = LibSodium.crypto_sign_bytes