Add unauthenticated secret/public key encryption.

This commit is contained in:
Didactic Drunk 2019-06-29 18:19:01 -07:00
parent 162cd72b0c
commit f038f9b52e
8 changed files with 57 additions and 16 deletions

View File

@ -27,7 +27,7 @@ describe Sodium::CryptoBox::SecretKey do
key1.public_key.bytes.should eq key2.public_key.bytes key1.public_key.bytes.should eq key2.public_key.bytes
end end
it "easy encrypt/decrypt" do it "authenticated easy encrypt/decrypt" do
data = "Hello World!" data = "Hello World!"
# Alice is the sender # Alice is the sender
@ -50,6 +50,21 @@ describe Sodium::CryptoBox::SecretKey do
end end
end end
it "unauthenticated seal encrypt/decrypt" do
data = "foo bar"
# Bob is the recipient
bob = Sodium::CryptoBox::SecretKey.new
# Encrypt a message for Bob using his public key. No signature.
encrypted = bob.public_key.encrypt data
# Decrypt the message using Bob's secret key.
decrypted = bob.decrypt encrypted
String.new(decrypted).should eq(data)
end
it "wipes keys" do it "wipes keys" do
check_wiped new_key_bytes check_wiped new_key_bytes
end end

View File

@ -4,6 +4,8 @@ module Sodium::CryptoBox
class Box class Box
include Wipe include Wipe
MAC_SIZE = LibSodium.crypto_box_macbytes
# BUG: precompute size # BUG: precompute size
@[Wipe::Var] @[Wipe::Var]
@bytes = Bytes.new(1) @bytes = Bytes.new(1)
@ -16,14 +18,14 @@ module Sodium::CryptoBox
encrypt_easy src.to_slice encrypt_easy src.to_slice
end end
def encrypt_easy(src : Bytes, dst = Bytes.new(src.bytesize + LibSodium::MAC_SIZE), nonce = Nonce.new) def encrypt_easy(src : Bytes, dst = Bytes.new(src.bytesize + MAC_SIZE), nonce = Nonce.new)
if LibSodium.crypto_box_easy(dst, src, src.bytesize, nonce.to_slice, @public_key.to_slice, @secret_key.to_slice) != 0 if LibSodium.crypto_box_easy(dst, src, src.bytesize, nonce.to_slice, @public_key.to_slice, @secret_key.to_slice) != 0
raise Error.new("crypto_box_easy") raise Error.new("crypto_box_easy")
end end
{nonce, dst} {nonce, dst}
end end
def decrypt_easy(src : Bytes, dst = Bytes.new(src.bytesize - LibSodium::MAC_SIZE), nonce = Nonce.new) : Bytes def decrypt_easy(src : Bytes, dst = Bytes.new(src.bytesize - MAC_SIZE), nonce = Nonce.new) : Bytes
if LibSodium.crypto_box_open_easy(dst, src, src.bytesize, nonce.to_slice, @public_key.to_slice, @secret_key.to_slice) != 0 if LibSodium.crypto_box_open_easy(dst, src, src.bytesize, nonce.to_slice, @public_key.to_slice, @secret_key.to_slice) != 0
raise Error::DecryptionFailed.new("crypto_box_open_easy") raise Error::DecryptionFailed.new("crypto_box_open_easy")
end end

View File

@ -4,6 +4,7 @@ module Sodium::CryptoBox
class PublicKey < Key class PublicKey < Key
include Wipe include Wipe
KEY_SIZE = LibSodium.crypto_box_publickeybytes KEY_SIZE = LibSodium.crypto_box_publickeybytes
SEAL_SIZE = LibSodium.crypto_box_sealbytes
getter bytes : Bytes getter bytes : Bytes
@ -18,5 +19,18 @@ module Sodium::CryptoBox
raise ArgumentError.new("Public key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}") raise ArgumentError.new("Public key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}")
end end
end end
# Anonymously send messages to a recipient given its public key.
# For authenticated message use `secret_key.box(recipient_public_key).encrypt`.
def encrypt(src)
encrypt src.to_slice
end
def encrypt(src : Bytes, dst : Bytes = Bytes.new(src.bytesize + SEAL_SIZE)) : Bytes
if LibSodium.crypto_box_seal(dst, src, src.bytesize, @bytes) != 0
raise Sodium::Error.new("crypto_box_seal")
end
dst
end
end end
end end

View File

@ -7,7 +7,7 @@ module Sodium::CryptoBox
include Wipe include Wipe
KEY_SIZE = LibSodium.crypto_box_secretkeybytes KEY_SIZE = LibSodium.crypto_box_secretkeybytes
SEED_SIZE = LibSodium.crypto_box_seedbytes SEED_SIZE = LibSodium.crypto_box_seedbytes
MAC_SIZE = LibSodium::MAC_SIZE SEAL_SIZE = LibSodium.crypto_box_sealbytes
getter public_key : PublicKey getter public_key : PublicKey
@ -51,7 +51,7 @@ module Sodium::CryptoBox
end end
end end
# Return a Box containing a precomputed shared secret for use with encryption/decryption. # Return a Box containing a precomputed shared secret for use with authenticated encryption/decryption.
def box(public_key) : Box def box(public_key) : Box
Box.new self, public_key Box.new self, public_key
end end
@ -65,5 +65,18 @@ module Sodium::CryptoBox
b.close b.close
end end
end end
# Anonymously receive messages without signatures.
# For authenticated messages use `secret_key.box(recipient_public_key).decrypt`.
def decrypt(src)
encrypt src.to_slice
end
def decrypt(src : Bytes, dst : Bytes = Bytes.new(src.bytesize - SEAL_SIZE)) : Bytes
if LibSodium.crypto_box_seal_open(dst, src, src.bytesize, @public_key.bytes, @bytes) != 0
raise Sodium::Error.new("crypto_box_seal_open")
end
dst
end
end end
end end

View File

@ -7,6 +7,7 @@ module Sodium
fun crypto_box_secretkeybytes : LibC::SizeT fun crypto_box_secretkeybytes : LibC::SizeT
fun crypto_box_seedbytes : LibC::SizeT fun crypto_box_seedbytes : LibC::SizeT
fun crypto_box_noncebytes : LibC::SizeT fun crypto_box_noncebytes : LibC::SizeT
fun crypto_box_sealbytes : LibC::SizeT
fun crypto_box_macbytes : LibC::SizeT fun crypto_box_macbytes : LibC::SizeT
fun crypto_sign_publickeybytes : LibC::SizeT fun crypto_sign_publickeybytes : LibC::SizeT
fun crypto_sign_secretkeybytes : LibC::SizeT fun crypto_sign_secretkeybytes : LibC::SizeT
@ -43,8 +44,6 @@ module Sodium
fun sodium_memzero(Pointer(LibC::UChar), LibC::SizeT) : Nil fun sodium_memzero(Pointer(LibC::UChar), LibC::SizeT) : Nil
NONCE_SIZE = crypto_box_noncebytes() NONCE_SIZE = crypto_box_noncebytes()
MAC_SIZE = crypto_box_macbytes()
SIGNATURE_SIZE = crypto_sign_bytes()
fun crypto_secretbox_easy( fun crypto_secretbox_easy(
output : Pointer(LibC::UChar), output : Pointer(LibC::UChar),
@ -225,8 +224,4 @@ module Sodium
if LibSodium.crypto_secretbox_noncebytes != LibSodium.crypto_box_noncebytes if LibSodium.crypto_secretbox_noncebytes != LibSodium.crypto_box_noncebytes
raise "Assumptions in this library regarding nonce sizes may not be valid" raise "Assumptions in this library regarding nonce sizes may not be valid"
end end
if LibSodium.crypto_secretbox_macbytes != LibSodium.crypto_box_macbytes
raise "Assumptions in this library regarding mac sizes may not be valid"
end
end end

View File

@ -15,7 +15,7 @@ module Sodium
class SecretBox < Key class SecretBox < Key
KEY_SIZE = LibSodium.crypto_secretbox_keybytes KEY_SIZE = LibSodium.crypto_secretbox_keybytes
NONCE_SIZE = LibSodium.crypto_secretbox_noncebytes NONCE_SIZE = LibSodium.crypto_secretbox_noncebytes
MAC_SIZE = LibSodium::MAC_SIZE MAC_SIZE = LibSodium.crypto_secretbox_macbytes
@[Wipe::Var] @[Wipe::Var]
property bytes : Bytes property bytes : Bytes

View File

@ -4,6 +4,7 @@ module Sodium
class Sign::PublicKey < Key class Sign::PublicKey < Key
include Wipe include Wipe
KEY_SIZE = LibSodium.crypto_sign_publickeybytes KEY_SIZE = LibSodium.crypto_sign_publickeybytes
SIG_SIZE = LibSodium.crypto_sign_bytes
getter bytes : Bytes getter bytes : Bytes
@ -26,7 +27,7 @@ module Sodium
end end
def verify_detached(message : Bytes, sig : Bytes) def verify_detached(message : Bytes, sig : Bytes)
raise ArgumentError.new("Signature must be #{LibSodium::SIGNATURE_SIZE} bytes, got #{sig.bytesize}") if sig.bytesize != LibSodium::SIGNATURE_SIZE raise ArgumentError.new("Signature must be #{SIG_SIZE} bytes, got #{sig.bytesize}") if sig.bytesize != SIG_SIZE
v = LibSodium.crypto_sign_verify_detached sig, message, message.bytesize, @bytes v = LibSodium.crypto_sign_verify_detached sig, message, message.bytesize, @bytes
if v != 0 if v != 0

View File

@ -9,6 +9,7 @@ module Sodium
# ``` # ```
class Sign::SecretKey < Sodium::Key class Sign::SecretKey < Sodium::Key
KEY_SIZE = LibSodium.crypto_sign_secretkeybytes KEY_SIZE = LibSodium.crypto_sign_secretkeybytes
SIG_SIZE = LibSodium.crypto_sign_bytes
SEED_SIZE = LibSodium.crypto_sign_seedbytes SEED_SIZE = LibSodium.crypto_sign_seedbytes
getter public_key : PublicKey getter public_key : PublicKey
@ -61,7 +62,7 @@ module Sodium
end end
def sign_detached(message : Bytes) def sign_detached(message : Bytes)
sig = Bytes.new(LibSodium::SIGNATURE_SIZE) sig = Bytes.new(SIG_SIZE)
if LibSodium.crypto_sign_detached(sig, out sig_len, message, message.bytesize, @bytes) != 0 if LibSodium.crypto_sign_detached(sig, out sig_len, message, message.bytesize, @bytes) != 0
raise Error.new("crypto_sign_detached") raise Error.new("crypto_sign_detached")
end end