diff --git a/README.md b/README.md index 1d3cce2..e4a4b71 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,13 @@ Crystal bindings for the [libsodium API](https://libsodium.gitbook.io/doc/) ## Features - [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) + - [x] ☑ [Crypto Box Easy](https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption) - [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) - Secret Box - - [x] [Combined mode](https://libsodium.gitbook.io/doc/secret-key_cryptography/authenticated_encryption) + - [x] ☑ [Combined mode](https://libsodium.gitbook.io/doc/secret-key_cryptography/authenticated_encryption) - [ ] [Detached mode](https://libsodium.gitbook.io/doc/secret-key_cryptography/authenticated_encryption) - [x] [Secret Stream](https://libsodium.gitbook.io/doc/secret-key_cryptography/secretstream) - [AEAD](https://libsodium.gitbook.io/doc/secret-key_cryptography/aead) @@ -60,6 +60,7 @@ Several features in libsodium are already provided by Crystal: * Random (Use [Random::Secure](https://crystal-lang.org/api/latest/Random/Secure.html)) * 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)) +* Hex conversion (Use [String#hexbytes](https://crystal-lang.org/api/latest/String.html#hexbytes%3ABytes-instance-method)) ## What should I use for my application? diff --git a/spec/sodium/crypto_box/secret_key_spec.cr b/spec/sodium/crypto_box/secret_key_spec.cr index 0f69379..6ba4eb3 100644 --- a/spec/sodium/crypto_box/secret_key_spec.cr +++ b/spec/sodium/crypto_box/secret_key_spec.cr @@ -1,6 +1,39 @@ require "../../spec_helper" require "../../../src/sodium/crypto_box/secret_key" +combined_test_vectors = [ + { + alice_sec: "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a", + alice_pub: "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a", + bob_sec: "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb", + bob_pub: "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f", + nonce: "69696ee955b62b73cd62bda875fc73d68219e0036b7a0b37", + plaintext: "be075fc53c81f2d5cf141316ebeb0c7b5228c52a4c62cbd44b66849b64244ffce5e" \ + "cbaaf33bd751a1ac728d45e6c61296cdc3c01233561f41db66cce314adb310e3be8" \ + "250c46f06dceea3a7fa1348057e2f6556ad6b1318a024a838f21af1fde048977eb4" \ + "8f59ffd4924ca1c60902e52f0a089bc76897040e082f937763848645e0705", + ciphertext: "f3ffc7703f9400e52a7dfb4b3d3305d98e993b9f48681273c29650ba32fc76ce483" \ + "32ea7164d96a4476fb8c531a1186ac0dfc17c98dce87b4da7f011ec48c97271d2c2" \ + "0f9b928fe2270d6fb863d51738b48eeee314a7cc8ab932164548e526ae902243685" \ + "17acfeabd6bb3732bc0e9da99832b61ca01b6de56244a9e88d5f9b37973f622a43d" \ + "14a6599b1f654cb45a74e355a5", + }, +] + +private def box_from_vec(vec) + alice = Sodium::CryptoBox::SecretKey.new vec[:alice_sec].hexbytes, vec[:alice_pub].hexbytes + bob = Sodium::CryptoBox::SecretKey.new vec[:bob_sec].hexbytes, vec[:bob_pub].hexbytes + nonce = Sodium::Nonce.new vec[:nonce].hexbytes + plaintext = vec[:plaintext].hexbytes + ciphertext = vec[:ciphertext].hexbytes + + alice.box(bob.public_key) do |box1| + bob.box(alice.public_key) do |box2| + yield box1, box2, nonce, plaintext, ciphertext + end + end +end + private def new_key_bytes Sodium::CryptoBox::SecretKey.new.to_slice end @@ -65,4 +98,16 @@ describe Sodium::CryptoBox::SecretKey do String.new(decrypted).should eq(data) end + + it "PyNaCl combined test vectors" do + combined_test_vectors.each do |vec| + box_from_vec(vec) do |box1, box2, nonce, plaintext, ciphertext| + encrypted, _ = box1.encrypt_easy plaintext, nonce: nonce + encrypted.should eq ciphertext + + decrypted = box2.decrypt_easy ciphertext, nonce: nonce + decrypted.should eq plaintext + end + end + end end diff --git a/spec/sodium/secret_box_spec.cr b/spec/sodium/secret_box_spec.cr index e879fb0..a43b495 100644 --- a/spec/sodium/secret_box_spec.cr +++ b/spec/sodium/secret_box_spec.cr @@ -1,17 +1,66 @@ require "../spec_helper" require "../../src/sodium/secret_box" +combined_test_vectors = [ + { + key: "1b27556473e985d462cd51197a9a46c76009549eac6474f206c4ee0844f68389", + nonce: "69696ee955b62b73cd62bda875fc73d68219e0036b7a0b37", + plaintext: "be075fc53c81f2d5cf141316ebeb0c7b5228c52a4c62cbd44b66849b64244ffce5e" \ + "cbaaf33bd751a1ac728d45e6c61296cdc3c01233561f41db66cce314adb310e3be8" \ + "250c46f06dceea3a7fa1348057e2f6556ad6b1318a024a838f21af1fde048977eb4" \ + "8f59ffd4924ca1c60902e52f0a089bc76897040e082f937763848645e0705", + ciphertext: "f3ffc7703f9400e52a7dfb4b3d3305d98e993b9f48681273c29650ba32fc76ce483" \ + "32ea7164d96a4476fb8c531a1186ac0dfc17c98dce87b4da7f011ec48c97271d2c2" \ + "0f9b928fe2270d6fb863d51738b48eeee314a7cc8ab932164548e526ae902243685" \ + "17acfeabd6bb3732bc0e9da99832b61ca01b6de56244a9e88d5f9b37973f622a43d" \ + "14a6599b1f654cb45a74e355a5", + }, +] + +private def box_from_test_vector(vec) + box = Sodium::SecretBox.new vec[:key].hexbytes + nonce = Sodium::Nonce.new vec[:nonce].hexbytes + plaintext = vec[:plaintext].hexbytes + ciphertext = vec[:ciphertext].hexbytes + + {box, nonce, plaintext, ciphertext} +end + describe Sodium::SecretBox do it "encrypts/decrypts" do - key = Sodium::SecretBox.new + box = Sodium::SecretBox.new message = "foobar" - encrypted, nonce = key.encrypt_easy message - decrypted = key.decrypt_easy encrypted, nonce + encrypted, nonce = box.encrypt_easy message + decrypted = box.decrypt_easy encrypted, nonce message.should eq String.new(decrypted) expect_raises(Sodium::Error::DecryptionFailed) do - key.decrypt_easy "badmsgbadmsgbadmsgbadmsgbadmsg".to_slice, nonce + box.decrypt_easy "badmsgbadmsgbadmsgbadmsgbadmsg".to_slice, nonce + end + end + + it "PyNaCl combined test vectors" do + combined_test_vectors.each do |vec| + box, nonce, plaintext, ciphertext = box_from_test_vector vec + + encrypted = box.encrypt_easy plaintext, nonce + encrypted.should eq ciphertext + + decrypted = box.decrypt_easy encrypted, nonce + plaintext.should eq decrypted + end + end + + pending "detached test vectors" do + detached_test_vectors.each do |vec| + box, nonce, plaintext, ciphertext = box_from_test_vector vec + + encrypted = box.encrypt_detached plaintext, nonce + encrypted.should eq ciphertext + + decrypted = box.decrypt_detached encrypted, nonce + plaintext.should eq decrypted end end end