diff --git a/spec/cox/secret_key_spec.cr b/spec/cox/secret_key_spec.cr new file mode 100644 index 0000000..0871d4e --- /dev/null +++ b/spec/cox/secret_key_spec.cr @@ -0,0 +1,16 @@ +require "../spec_helper" + +describe Cox::SecretKey do + it "encrypts/decrypts" do + key = Cox::SecretKey.random + + message = "foobar" + encrypted, nonce = key.encrypt_easy message + decrypted = key.decrypt_easy encrypted, nonce + message.should eq String.new(decrypted) + + expect_raises(Cox::DecryptionFailed) do + key.decrypt_easy "badmsgbadmsgbadmsgbadmsgbadmsg".to_slice, nonce + end + end +end diff --git a/src/cox/lib_sodium.cr b/src/cox/lib_sodium.cr index 03863c4..33c78a4 100644 --- a/src/cox/lib_sodium.cr +++ b/src/cox/lib_sodium.cr @@ -33,6 +33,22 @@ module Cox KDF_CONTEXT_BYTES = crypto_kdf_contextbytes() PWHASH_STR_BYTES = crypto_pwhash_strbytes() + fun crypto_secretbox_easy( + output : Pointer(LibC::UChar), + data : Pointer(LibC::UChar), + data_size : LibC::ULongLong, + nonce : Pointer(LibC::UChar), + key : Pointer(LibC::UChar), + ) : LibC::Int + + fun crypto_secretbox_open_easy( + output : Pointer(LibC::UChar), + data : Pointer(LibC::UChar), + data_size : LibC::ULongLong, + nonce : Pointer(LibC::UChar), + key : Pointer(LibC::UChar), + ) : LibC::Int + fun crypto_box_keypair( public_key_output : Pointer(LibC::UChar), secret_key_output : Pointer(LibC::UChar) diff --git a/src/cox/secret_key.cr b/src/cox/secret_key.cr index 592dd93..d449ddf 100644 --- a/src/cox/secret_key.cr +++ b/src/cox/secret_key.cr @@ -11,5 +11,43 @@ module Cox raise ArgumentError.new("Secret key must be #{KEY_LENGTH} bytes, got #{bytes.bytesize}") end end + + def self.random + new Random::Secure.random_bytes(KEY_LENGTH) + end + + def encrypt_easy(data) + encrypt_easy data.to_slice + end + + def encrypt_easy(data, nonce : Nonce) + encrypt_easy data.to_slice, nonce + end + + def encrypt_easy(data : Bytes) + nonce = Nonce.new + output = encrypt_easy data, nonce + {output, nonce} + end + + def encrypt_easy(data : Bytes, nonce : Nonce) : Bytes + output = Bytes.new(data.bytesize + LibSodium::MAC_BYTES) + if LibSodium.crypto_secretbox_easy(output, data, data.bytesize, nonce.pointer, @bytes) != 0 + raise Cox::Error.new("crypto_secretbox_easy") + end + output + end + + def decrypt_easy(data : Bytes, nonce : Nonce) : Bytes + output_size = data.bytesize - LibSodium::MAC_BYTES + raise Cox::DecryptionFailed.new("encrypted data too small #{data.bytesize}") if output_size <= 0 + output = Bytes.new output_size + if LibSodium.crypto_secretbox_open_easy(output, data, data.bytesize, nonce.pointer, @bytes) != 0 + raise Cox::DecryptionFailed.new("crypto_secretbox_easy") + end + output + end + + # TODO: encrypt_detached end end