From 0eb4a8991ae7405d179c49a00b6ba2a97548ec43 Mon Sep 17 00:00:00 2001 From: Didactic Drunk <1479616+didactic-drunk@users.noreply.github.com> Date: Fri, 28 Jun 2019 03:30:33 -0700 Subject: [PATCH] Breaking API changes: SignSecretKey rename to Sign::SecretKey SignPublicKey rename to Sign::PublicKey SignKeyPair removed. Use Sign::SecretKey instead. Cox.sign_detached moved to Sign::SecretKey#sign_detached Cox.verify_detached moved to Sign::PublicKey#verify_detached verify_detached raises on failure instead of using a return value. More validation of data sizes. --- README.md | 14 +++++++---- spec/cox/secret_key_spec.cr | 2 +- spec/cox/sign/secret_key.cr | 22 ++++++++++++++++++ spec/cox_spec.cr | 14 ----------- src/cox.cr | 31 ++++--------------------- src/cox/lib_sodium.cr | 2 +- src/cox/secret_key.cr | 4 ++-- src/cox/sign/public_key.cr | 28 ++++++++++++++++++++++ src/cox/sign/secret_key.cr | 46 +++++++++++++++++++++++++++++++++++++ src/cox/sign_key_pair.cr | 24 ------------------- src/cox/sign_public_key.cr | 15 ------------ src/cox/sign_secret_key.cr | 15 ------------ 12 files changed, 114 insertions(+), 103 deletions(-) create mode 100644 spec/cox/sign/secret_key.cr create mode 100644 src/cox/sign/public_key.cr create mode 100644 src/cox/sign/secret_key.cr delete mode 100644 src/cox/sign_key_pair.cr delete mode 100644 src/cox/sign_public_key.cr delete mode 100644 src/cox/sign_secret_key.cr diff --git a/README.md b/README.md index 835943b..9a0a4ed 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Updated Crystal bindings for the [libsodium API](https://libsodium.gitbook.io/do - Public-Key Cryptography - [x] Crypto Box Easy - [ ] Sealed Box - - [x] Combined Signatures + - [ ] Combined Signatures - [x] Detached Signatures - [Secret-Key Cryptography](https://libsodium.gitbook.io/doc/secret-key_cryptography) - Secret Box @@ -89,13 +89,17 @@ String.new(decrypted) # => "Hello World!" ```crystal message = "Hello World!" -signing_pair = Cox::SignKeyPair.new +secret_key = Cox::Sign::SecretKey.new # Sign the message -signature = Cox.sign_detached(message, signing_pair.secret) +signature = secret_key.sign_detached message -# And verify -Cox.verify_detached(signature, message, signing_pair.public) # => true +# Send secret_key.public_key to the recipient + +public_key = Cox::Sign::PublicKey.new key_bytes + +# raises Cox::Error::VerificationFailed on failure. +public_key.verify_detached message, signature ``` ### Secret Key Encryption diff --git a/spec/cox/secret_key_spec.cr b/spec/cox/secret_key_spec.cr index 0871d4e..666dfb9 100644 --- a/spec/cox/secret_key_spec.cr +++ b/spec/cox/secret_key_spec.cr @@ -9,7 +9,7 @@ describe Cox::SecretKey do decrypted = key.decrypt_easy encrypted, nonce message.should eq String.new(decrypted) - expect_raises(Cox::DecryptionFailed) do + expect_raises(Cox::Error::DecryptionFailed) do key.decrypt_easy "badmsgbadmsgbadmsgbadmsgbadmsg".to_slice, nonce end end diff --git a/spec/cox/sign/secret_key.cr b/spec/cox/sign/secret_key.cr new file mode 100644 index 0000000..6d91c12 --- /dev/null +++ b/spec/cox/sign/secret_key.cr @@ -0,0 +1,22 @@ +require "../../spec_helper" +require "../../../src/cox/sign/secret_key" + +describe Cox::Sign::SecretKey do + it "signs and verifies" do + message = "foo" + skey = Cox::Sign::SecretKey.new + sig = skey.sign_detached message + + skey.public_key.verify_detached message, sig + end + + it "signs and fails" do + message = "foo" + skey = Cox::Sign::SecretKey.new + sig = skey.sign_detached message + + expect_raises Cox::Error::VerificationFailed do + skey.public_key.verify_detached "bar", sig + end + end +end diff --git a/spec/cox_spec.cr b/spec/cox_spec.cr index d0afed6..518ec37 100644 --- a/spec/cox_spec.cr +++ b/spec/cox_spec.cr @@ -22,18 +22,4 @@ describe Cox do String.new(decrypted).should eq(data) end - - it "works for signing" do - message = "test" - - signing_pair = Cox::SignKeyPair.new - - # Create signature using the secret key - signature = Cox.sign_detached(message, signing_pair.secret) - - # Verify the signature on the message - verified = Cox.verify_detached(signature, message, signing_pair.public) - - verified.should eq(true) - end end diff --git a/src/cox.cr b/src/cox.cr index ca2fdda..61caeab 100644 --- a/src/cox.cr +++ b/src/cox.cr @@ -2,12 +2,11 @@ require "random/secure" module Cox class Error < ::Exception - end + class VerificationFailed < Error + end - class VerificationFailed < Error - end - - class DecryptionFailed < Error + class DecryptionFailed < Error + end end end @@ -34,30 +33,10 @@ module Cox data_size = data_buffer.bytesize output_buffer = Bytes.new(data_buffer.bytesize - LibSodium::MAC_SIZE) if LibSodium.crypto_box_open_easy(output_buffer.to_slice, data_buffer.to_slice, data_size, nonce.to_slice, sender_public_key.to_slice, recipient_secret_key.to_slice) != 0 - raise DecryptionFailed.new("crypto_box_open_easy") + raise Error::DecryptionFailed.new("crypto_box_open_easy") end output_buffer end - - def self.sign_detached(message, secret_key : SignSecretKey) - message_buffer = message.to_slice - message_buffer_size = message_buffer.bytesize - signature_output_buffer = Bytes.new(LibSodium::SIGNATURE_SIZE) - - if LibSodium.crypto_sign_detached(signature_output_buffer.to_slice, 0, message_buffer.to_slice, message_buffer_size, secret_key.to_slice) != 0 - raise Error.new("crypto_sign_detached") - end - signature_output_buffer - end - - def self.verify_detached(signature, message, public_key : SignPublicKey) - signature_buffer = signature.to_slice - message_buffer = message.to_slice - message_buffer_size = message_buffer.bytesize - - verified = LibSodium.crypto_sign_verify_detached(signature_buffer.to_slice, message_buffer.to_slice, message_buffer_size, public_key.to_slice) - verified.zero? - end end if Cox::LibSodium.sodium_init == -1 diff --git a/src/cox/lib_sodium.cr b/src/cox/lib_sodium.cr index e2c6d91..1e3bc0c 100644 --- a/src/cox/lib_sodium.cr +++ b/src/cox/lib_sodium.cr @@ -108,7 +108,7 @@ module Cox fun crypto_sign_detached( signature_output : Pointer(LibC::UChar), - signature_output_size : LibC::ULongLong, + signature_output_size : Pointer(LibC::ULongLong), message : Pointer(LibC::UChar), message_size : LibC::ULongLong, secret_key : Pointer(LibC::UChar) diff --git a/src/cox/secret_key.cr b/src/cox/secret_key.cr index f29bed0..6d491d6 100644 --- a/src/cox/secret_key.cr +++ b/src/cox/secret_key.cr @@ -48,7 +48,7 @@ module Cox def decrypt_easy(data : Bytes, nonce : Nonce) : Bytes output_size = data.bytesize - MAC_SIZE - raise Cox::DecryptionFailed.new("encrypted data too small #{data.bytesize}") if output_size <= 0 + raise Cox::Error::DecryptionFailed.new("encrypted data too small #{data.bytesize}") if output_size <= 0 output = Bytes.new output_size decrypt_easy(data, output, nonce) end @@ -58,7 +58,7 @@ module Cox raise ArgumentError.new("dst.bytesize must be src.bytesize - MAC_SIZE, got #{dst.bytesize}") end if LibSodium.crypto_secretbox_open_easy(dst, src, src.bytesize, nonce.to_slice, @bytes) != 0 - raise Cox::DecryptionFailed.new("crypto_secretbox_easy") + raise Cox::Error::DecryptionFailed.new("crypto_secretbox_easy") end dst end diff --git a/src/cox/sign/public_key.cr b/src/cox/sign/public_key.cr new file mode 100644 index 0000000..163e859 --- /dev/null +++ b/src/cox/sign/public_key.cr @@ -0,0 +1,28 @@ +require "../lib_sodium" + +module Cox + class Sign::PublicKey < Key + property bytes : Bytes + + KEY_SIZE = LibSodium::PUBLIC_SIGN_SIZE + + def initialize(@bytes : Bytes) + if bytes.bytesize != KEY_SIZE + raise ArgumentError.new("Public key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}") + end + end + + def verify_detached(message, sig : Bytes) + verify_detached message.to_slice, sig + end + + def verify_detached(message : Bytes, sig : Bytes) + raise ArgumentError.new("Signature must be #{LibSodium::SIGNATURE_SIZE} bytes, got #{sig.bytesize}") + + v = LibSodium.crypto_sign_verify_detached sig, message, message.bytesize, @bytes + if v != 0 + raise Cox::Error::VerificationFailed.new("crypto_sign_verify_detached") + end + end + end +end diff --git a/src/cox/sign/secret_key.cr b/src/cox/sign/secret_key.cr new file mode 100644 index 0000000..5e30eb2 --- /dev/null +++ b/src/cox/sign/secret_key.cr @@ -0,0 +1,46 @@ +require "../lib_sodium" + +module Cox + class Sign::SecretKey < Cox::Key + KEY_SIZE = LibSodium::SECRET_SIGN_SIZE + + getter bytes : Bytes + getter public_key + + # Generates a new secret/public key pair. + def initialize + pkey = Bytes.new(Sign::PublicKey::KEY_SIZE) + @bytes = Bytes.new(KEY_SIZE) + @public_key = PublicKey.new pkey + LibSodium.crypto_sign_keypair pkey, @bytes + end + + # Use existing Private and Public keys. + def initialize(@bytes : Bytes, pkey : Bytes) + raise ArgumentError.new("Secret sign key must be #{KEY_SIZE}, got #{@bytes.bytesize}") + @public_key = PublicKey.new pkey + end + + # def initialize(@bytes : Bytes) + # if bytes.bytesize != KEY_SIZE + # raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}") + # end + # BUG: fix + # @public_key = PublicKey.new Bytes.new(100) + # raise "Needs crypto_sign_ed25519_sk_to_pk" + # Also needs to differentiate from seed as a single parameter + # end + + def sign_detached(message) + sign_detached message.to_slice + end + + def sign_detached(message : Bytes) + sig = Bytes.new(LibSodium::SIGNATURE_SIZE) + if LibSodium.crypto_sign_detached(sig, out sig_len, message, message.bytesize, @bytes) != 0 + raise Error.new("crypto_sign_detached") + end + sig + end + end +end diff --git a/src/cox/sign_key_pair.cr b/src/cox/sign_key_pair.cr deleted file mode 100644 index f58b00e..0000000 --- a/src/cox/sign_key_pair.cr +++ /dev/null @@ -1,24 +0,0 @@ -require "./lib_sodium" - -module Cox - class SignKeyPair - property public : SignPublicKey - property secret : SignSecretKey - - def initialize(@public, @secret) - end - - def self.new(pub : Bytes, sec : Bytes) - new(SignPublicKey.new(pub), SignSecretKey.new(sec)) - end - - def self.new - public_key = Bytes.new(SignPublicKey::KEY_SIZE) - secret_key = Bytes.new(SignSecretKey::KEY_SIZE) - - LibSodium.crypto_sign_keypair(public_key.to_unsafe, secret_key.to_unsafe) - - new(public_key, secret_key) - end - end -end diff --git a/src/cox/sign_public_key.cr b/src/cox/sign_public_key.cr deleted file mode 100644 index 64fcfba..0000000 --- a/src/cox/sign_public_key.cr +++ /dev/null @@ -1,15 +0,0 @@ -require "./lib_sodium" - -module Cox - class SignPublicKey < Key - property bytes : Bytes - - KEY_SIZE = LibSodium::PUBLIC_SIGN_SIZE - - def initialize(@bytes : Bytes) - if bytes.bytesize != KEY_SIZE - raise ArgumentError.new("Public key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}") - end - end - end -end diff --git a/src/cox/sign_secret_key.cr b/src/cox/sign_secret_key.cr deleted file mode 100644 index be36c0c..0000000 --- a/src/cox/sign_secret_key.cr +++ /dev/null @@ -1,15 +0,0 @@ -require "./lib_sodium" - -module Cox - class SignSecretKey < Key - property bytes : Bytes - - KEY_SIZE = LibSodium::SECRET_SIGN_SIZE - - def initialize(@bytes : Bytes) - if bytes.bytesize != KEY_SIZE - raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}") - end - end - end -end