Rename Sodium::Pwhash#key_derive to #derive_key. Rename Sodium::Pwhash#kdf_derive to #derive_kdf. Rename Sodium::CryptoBox#encrypt_easy to #encrypt. Rename Sodium::CryptoBox#decrypt_easy to #decrypt. Rename Sodium::SecretBox#encrypt_easy to #encrypt. Rename Sodium::SecretBox#decrypt_easy to #decrypt. Remove redundant Sodium::SecretBox#encrypt methods.
81 lines
2.6 KiB
Crystal
81 lines
2.6 KiB
Crystal
require "./lib_sodium"
|
|
require "./key"
|
|
require "./nonce"
|
|
|
|
module Sodium
|
|
# [https://libsodium.gitbook.io/doc/secret-key_cryptography](https://libsodium.gitbook.io/doc/secret-key_cryptography)
|
|
#
|
|
#
|
|
# ```crystal
|
|
# key = Sodium::SecretBox.new
|
|
# message = "foobar"
|
|
# encrypted, nonce = key.encrypt message
|
|
#
|
|
# # On the other side.
|
|
# key = Sodium::SecretBox.new key
|
|
# message = key.decrypt encrypted, nonce
|
|
# ```
|
|
class SecretBox < Key
|
|
KEY_SIZE = LibSodium.crypto_secretbox_keybytes.to_i
|
|
NONCE_SIZE = LibSodium.crypto_secretbox_noncebytes.to_i
|
|
MAC_SIZE = LibSodium.crypto_secretbox_macbytes.to_i
|
|
|
|
delegate to_slice, to: @buf
|
|
|
|
# Generate a new random key held in a SecureBuffer.
|
|
def initialize
|
|
@buf = SecureBuffer.random KEY_SIZE
|
|
end
|
|
|
|
# Use an existing SecureBuffer.
|
|
def initialize(@buf : SecureBuffer)
|
|
if @buf.bytesize != KEY_SIZE
|
|
raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{@buf.bytesize}")
|
|
end
|
|
@buf.readonly
|
|
end
|
|
|
|
# Copy bytes to a new SecureBuffer
|
|
#
|
|
# Optionally erases bytes after copying if erase is set
|
|
def initialize(bytes : Bytes, erase = false)
|
|
if bytes.bytesize != KEY_SIZE
|
|
raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}")
|
|
end
|
|
@buf = SecureBuffer.new bytes, erase: erase
|
|
end
|
|
|
|
def encrypt(data)
|
|
encrypt data.to_slice
|
|
end
|
|
|
|
def encrypt(src : Bytes, dst : Bytes = Bytes.new(src.bytesize + MAC_SIZE), nonce : Nonce = Nonce.new) : {Bytes, Nonce}
|
|
if dst.bytesize != (src.bytesize + MAC_SIZE)
|
|
raise ArgumentError.new("dst.bytesize must be src.bytesize + MAC_SIZE, got #{dst.bytesize}")
|
|
end
|
|
if LibSodium.crypto_secretbox_easy(dst, src, src.bytesize, nonce.to_slice, self.to_slice) != 0
|
|
raise Sodium::Error.new("crypto_secretbox_easy")
|
|
end
|
|
{dst, nonce}
|
|
end
|
|
|
|
def decrypt(src : Bytes, nonce : Nonce) : Bytes
|
|
dst_size = src.bytesize - MAC_SIZE
|
|
raise Sodium::Error::DecryptionFailed.new("encrypted data too small #{src.bytesize}") if dst_size <= 0
|
|
dst = Bytes.new dst_size
|
|
decrypt(src, dst, nonce)
|
|
end
|
|
|
|
def decrypt(src : Bytes, dst : Bytes, nonce : Nonce) : Bytes
|
|
if dst.bytesize != (src.bytesize - MAC_SIZE)
|
|
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, self.to_slice) != 0
|
|
raise Sodium::Error::DecryptionFailed.new("crypto_secretbox_easy")
|
|
end
|
|
dst
|
|
end
|
|
|
|
# TODO: encrypt_detached
|
|
end
|
|
end
|