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.master
parent
da8f97ae47
commit
0eb4a8991a
14
README.md
14
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
31
src/cox.cr
31
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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue