From 3fa19b57e3f233556bbdc6cb6d723afa07792216 Mon Sep 17 00:00:00 2001 From: Dave Date: Sun, 11 Feb 2018 23:06:34 -0800 Subject: [PATCH 1/4] added basic libsodium public key signature sign/verify functionality --- spec/cox_spec.cr | 16 +++++++++++++++- src/cox.cr | 18 ++++++++++++++++++ src/cox/lib_sodium.cr | 26 ++++++++++++++++++++++++++ src/cox/sign_key_pair.cr | 24 ++++++++++++++++++++++++ src/cox/sign_public_key.cr | 15 +++++++++++++++ src/cox/sign_secret_key.cr | 15 +++++++++++++++ src/cox/signature.cr | 15 +++++++++++++++ 7 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/cox/sign_key_pair.cr create mode 100644 src/cox/sign_public_key.cr create mode 100644 src/cox/sign_secret_key.cr create mode 100644 src/cox/signature.cr diff --git a/spec/cox_spec.cr b/spec/cox_spec.cr index 1195ac9..c1c416c 100644 --- a/spec/cox_spec.cr +++ b/spec/cox_spec.cr @@ -3,7 +3,7 @@ require "./spec_helper" describe Cox do # TODO: Write tests - it "works" do + it "works for encrypting" do data = "Hello World!" # Alice is the sender @@ -22,4 +22,18 @@ 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(message, signing_pair.secret) + + # Verify the signature on the message + verified = Cox.verify(signature, message, signing_pair.public) + + verified.should eq(true) + end end diff --git a/src/cox.cr b/src/cox.cr index 21c93df..ecee5c9 100644 --- a/src/cox.cr +++ b/src/cox.cr @@ -22,6 +22,24 @@ module Cox LibSodium.crypto_box_open_easy(output_buffer.to_unsafe, data_buffer.to_unsafe, data_size, nonce.pointer, sender_public_key.pointer, recipient_secret_key.pointer) output_buffer end + + def self.sign(message, secret_key : SignSecretKey) + message_buffer = message.to_slice + message_buffer_size = message_buffer.bytesize + signature_output_buffer = Bytes.new(LibSodium::SIGNATURE_BYTES) + + LibSodium.crypto_sign_detached(signature_output_buffer.to_unsafe, 0, message_buffer.to_unsafe, message_buffer_size, secret_key.pointer) + signature_output_buffer + end + + def self.verify(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_unsafe, message_buffer.to_unsafe, message_buffer_size, public_key.pointer) + 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 7522dbc..020e97c 100644 --- a/src/cox/lib_sodium.cr +++ b/src/cox/lib_sodium.cr @@ -7,11 +7,17 @@ module Cox fun crypto_box_secretkeybytes() : LibC::SizeT fun crypto_box_noncebytes() : LibC::SizeT fun crypto_box_macbytes() : LibC::SizeT + fun crypto_sign_publickeybytes() : LibC::SizeT + fun crypto_sign_secretkeybytes() : LibC::SizeT + fun crypto_sign_bytes() : LibC::SizeT PUBLIC_KEY_BYTES = crypto_box_publickeybytes() SECRET_KEY_BYTES = crypto_box_secretkeybytes() NONCE_BYTES = crypto_box_macbytes() MAC_BYTES = crypto_box_macbytes() + PUBLIC_SIGN_BYTES = crypto_sign_publickeybytes() + SECRET_SIGN_BYTES = crypto_sign_secretkeybytes() + SIGNATURE_BYTES = crypto_sign_bytes() fun crypto_box_keypair( public_key_output : Pointer(LibC::UChar), @@ -35,5 +41,25 @@ module Cox sender_public_key : Pointer(LibC::UChar), recipient_secret_key : Pointer(LibC::UChar) ) : LibC::Int + + fun crypto_sign_keypair( + public_key_output : Pointer(LibC::UChar), + secret_key_output : Pointer(LibC::UChar) + ) : LibC::Int + + fun crypto_sign_detached( + signature_output : Pointer(LibC::UChar), + signature_output_size : LibC::ULongLong, + message : Pointer(LibC::UChar), + message_size : LibC::ULongLong, + secret_key : Pointer(LibC::UChar) + ) : LibC::Int + + fun crypto_sign_verify_detached( + signature : Pointer(LibC::UChar), + message : Pointer(LibC::UChar), + message_size : LibC::ULongLong, + public_key : Pointer(LibC::UChar) + ) : LibC::Int end end diff --git a/src/cox/sign_key_pair.cr b/src/cox/sign_key_pair.cr new file mode 100644 index 0000000..0b64882 --- /dev/null +++ b/src/cox/sign_key_pair.cr @@ -0,0 +1,24 @@ +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_LENGTH) + secret_key = Bytes.new(SignSecretKey::KEY_LENGTH) + + 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 new file mode 100644 index 0000000..030c3c7 --- /dev/null +++ b/src/cox/sign_public_key.cr @@ -0,0 +1,15 @@ +require "./lib_sodium" + +module Cox + class SignPublicKey < Key + property bytes : Bytes + + KEY_LENGTH = LibSodium::PUBLIC_SIGN_BYTES + + def initialize(@bytes : Bytes) + if bytes.bytesize != KEY_LENGTH + raise ArgumentError.new("Public key must be #{KEY_LENGTH} bytes, got #{bytes.bytesize}") + 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..332099b --- /dev/null +++ b/src/cox/sign_secret_key.cr @@ -0,0 +1,15 @@ +require "./lib_sodium" + +module Cox + class SignSecretKey < Key + property bytes : Bytes + + KEY_LENGTH = LibSodium::SECRET_SIGN_BYTES + + def initialize(@bytes : Bytes) + if bytes.bytesize != KEY_LENGTH + raise ArgumentError.new("Secret key must be #{KEY_LENGTH} bytes, got #{bytes.bytesize}") + end + end + end +end diff --git a/src/cox/signature.cr b/src/cox/signature.cr new file mode 100644 index 0000000..1214638 --- /dev/null +++ b/src/cox/signature.cr @@ -0,0 +1,15 @@ +require "./lib_sodium" + +module Cox + class Signature + property bytes : Bytes + + KEY_LENGTH = LibSodium::SIGNATURE_BYTES + + def initialize(@bytes : Bytes) + if bytes.bytesize != KEY_LENGTH + raise ArgumentError.new("Signature must be #{KEY_LENGTH} bytes, got #{bytes.bytesize}") + end + end + end +end From e65a47a4ac2bd343a938a69288577878d3bf5296 Mon Sep 17 00:00:00 2001 From: Dave Date: Sun, 11 Feb 2018 23:18:45 -0800 Subject: [PATCH 2/4] updated Usage in README and cleaned up libsodium bindings --- README.md | 14 ++++++++++++++ src/cox/lib_sodium.cr | 20 ++++++++++---------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f95a702..d8f7c0f 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,19 @@ nonce, encrypted = Cox.encrypt(data, bob.public, alice.secret) decrypted = Cox.decrypt(encrypted, nonce, alice.public, bob.secret) String.new(decrypted) # => "Hello World!" + + +# Public key signing + +message = "Hello World!" + +signing_pair = Cox::SignKeyPair.new + +# Sign the message +signature = Cox.sign(message, signing_pair.secret) + +# And verify +Cox.verify(signature, message, signing_pair.public) # => true ``` ## Contributing @@ -52,3 +65,4 @@ String.new(decrypted) # => "Hello World!" ## Contributors - [andrewhamon](https://github.com/andrewhamon) Andrew Hamon - creator, maintainer +- [dorkrawk](https://github.com/dorkrawk) Dave Schwantes - contributor diff --git a/src/cox/lib_sodium.cr b/src/cox/lib_sodium.cr index 020e97c..8139fb0 100644 --- a/src/cox/lib_sodium.cr +++ b/src/cox/lib_sodium.cr @@ -3,21 +3,21 @@ module Cox lib LibSodium fun sodium_init() : LibC::Int - fun crypto_box_publickeybytes() : LibC::SizeT - fun crypto_box_secretkeybytes() : LibC::SizeT - fun crypto_box_noncebytes() : LibC::SizeT - fun crypto_box_macbytes() : LibC::SizeT + fun crypto_box_publickeybytes() : LibC::SizeT + fun crypto_box_secretkeybytes() : LibC::SizeT + fun crypto_box_noncebytes() : LibC::SizeT + fun crypto_box_macbytes() : LibC::SizeT fun crypto_sign_publickeybytes() : LibC::SizeT fun crypto_sign_secretkeybytes() : LibC::SizeT - fun crypto_sign_bytes() : LibC::SizeT + fun crypto_sign_bytes() : LibC::SizeT - PUBLIC_KEY_BYTES = crypto_box_publickeybytes() - SECRET_KEY_BYTES = crypto_box_secretkeybytes() - NONCE_BYTES = crypto_box_macbytes() - MAC_BYTES = crypto_box_macbytes() + PUBLIC_KEY_BYTES = crypto_box_publickeybytes() + SECRET_KEY_BYTES = crypto_box_secretkeybytes() + NONCE_BYTES = crypto_box_macbytes() + MAC_BYTES = crypto_box_macbytes() PUBLIC_SIGN_BYTES = crypto_sign_publickeybytes() SECRET_SIGN_BYTES = crypto_sign_secretkeybytes() - SIGNATURE_BYTES = crypto_sign_bytes() + SIGNATURE_BYTES = crypto_sign_bytes() fun crypto_box_keypair( public_key_output : Pointer(LibC::UChar), From 0f68a21481a5014d2cb5f47ce6ad4cb899bc8f55 Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 13 Feb 2018 20:20:27 -0800 Subject: [PATCH 3/4] Get rid of Signature model and change sign and verify to sign_detatched and verify_detached --- spec/cox_spec.cr | 6 +++--- src/cox.cr | 4 ++-- src/cox/signature.cr | 15 --------------- 3 files changed, 5 insertions(+), 20 deletions(-) delete mode 100644 src/cox/signature.cr diff --git a/spec/cox_spec.cr b/spec/cox_spec.cr index c1c416c..d0afed6 100644 --- a/spec/cox_spec.cr +++ b/spec/cox_spec.cr @@ -27,12 +27,12 @@ describe Cox do message = "test" signing_pair = Cox::SignKeyPair.new - + # Create signature using the secret key - signature = Cox.sign(message, signing_pair.secret) + signature = Cox.sign_detached(message, signing_pair.secret) # Verify the signature on the message - verified = Cox.verify(signature, message, signing_pair.public) + verified = Cox.verify_detached(signature, message, signing_pair.public) verified.should eq(true) end diff --git a/src/cox.cr b/src/cox.cr index ecee5c9..1e5fe1a 100644 --- a/src/cox.cr +++ b/src/cox.cr @@ -23,7 +23,7 @@ module Cox output_buffer end - def self.sign(message, secret_key : SignSecretKey) + 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_BYTES) @@ -32,7 +32,7 @@ module Cox signature_output_buffer end - def self.verify(signature, message, public_key : SignPublicKey) + 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 diff --git a/src/cox/signature.cr b/src/cox/signature.cr deleted file mode 100644 index 1214638..0000000 --- a/src/cox/signature.cr +++ /dev/null @@ -1,15 +0,0 @@ -require "./lib_sodium" - -module Cox - class Signature - property bytes : Bytes - - KEY_LENGTH = LibSodium::SIGNATURE_BYTES - - def initialize(@bytes : Bytes) - if bytes.bytesize != KEY_LENGTH - raise ArgumentError.new("Signature must be #{KEY_LENGTH} bytes, got #{bytes.bytesize}") - end - end - end -end From 59a58d9c9a72515f8e26a3ffbef42028aaff8ff5 Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 13 Feb 2018 20:23:34 -0800 Subject: [PATCH 4/4] update README to reflect new sign/verify method names --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d8f7c0f..933e792 100644 --- a/README.md +++ b/README.md @@ -48,10 +48,10 @@ message = "Hello World!" signing_pair = Cox::SignKeyPair.new # Sign the message -signature = Cox.sign(message, signing_pair.secret) +signature = Cox.sign_detached(message, signing_pair.secret) # And verify -Cox.verify(signature, message, signing_pair.public) # => true +Cox.verify_detached(signature, message, signing_pair.public) # => true ``` ## Contributing