Sodium::Cipher::Aead::XChaCha20Poly1305Ietf Always use SecureBuffer. Rename.

master
Didactic Drunk 2019-09-13 22:39:29 -07:00
parent b212f6bacd
commit d0f3100689
2 changed files with 27 additions and 14 deletions

View File

@ -26,7 +26,7 @@ private def box_from_test_vector(vec)
{box, nonce, plaintext, ciphertext} {box, nonce, plaintext, ciphertext}
end end
{% for name in %w(Xchacha20Poly1305Ietf) %} {% for name in %w(XChaCha20Poly1305Ietf) %}
# TODO: verify against test vectors. # TODO: verify against test vectors.
describe Sodium::Cipher::Aead::{{ name.id }} do describe Sodium::Cipher::Aead::{{ name.id }} do
it "encrypts/decrypts in combined mode" do it "encrypts/decrypts in combined mode" do

View File

@ -4,17 +4,24 @@ require "../../nonce"
module Sodium::Cipher::Aead module Sodium::Cipher::Aead
abstract class Chalsa abstract class Chalsa
@key : Bytes | SecureBuffer @key : SecureBuffer
# Initializes with a new random key.
def initialize def initialize
@key = SecureBuffer.random key_size @key = SecureBuffer.random key_size
end end
def initialize(@key) def initialize(@key : Securebuffer)
raise ArgumentError.new("key size mismatch, got #{@key.bytesize}, wanted #{key_size}") if @key.bytesize != key_size raise ArgumentError.new("key size mismatch, got #{@key.bytesize}, wanted #{key_size}") if @key.bytesize != key_size
@key.readonly
end end
# Encrypts data and returns {ciphertext, nonce} def initialize(bytes : Bytes, erase = false)
raise ArgumentError.new("key size mismatch, got #{bytes.bytesize}, wanted #{key_size}") if bytes.bytesize != key_size
@key = SecureBuffer.new bytes, erase: erase
end
# Encrypts `src` and returns {ciphertext, nonce}
def encrypt(src, dst : Bytes? = nil, *, nonce = nil, additional = nil) def encrypt(src, dst : Bytes? = nil, *, nonce = nil, additional = nil)
{Bytes, Nonce} {Bytes, Nonce}
offset = src.bytesize offset = src.bytesize
@ -24,7 +31,9 @@ module Sodium::Cipher::Aead
{dst, nonce} {dst, nonce}
end end
# Decrypts data and returns plaintext # Decrypts `src` and returns plaintext
# Must supply `nonce`
# Must supply `additional` if supplied to #encrypt
def decrypt(src, dst : Bytes? = nil, *, nonce : Nonce, additional = nil) : Bytes def decrypt(src, dst : Bytes? = nil, *, nonce : Nonce, additional = nil) : Bytes
src = src.to_slice src = src.to_slice
offset = src.bytesize - mac_size offset = src.bytesize - mac_size
@ -33,7 +42,9 @@ module Sodium::Cipher::Aead
decrypt_detached src[0, offset], dst, nonce: nonce, mac: mac, additional: additional decrypt_detached src[0, offset], dst, nonce: nonce, mac: mac, additional: additional
end end
# Decrypts data and returns plaintext # Decrypts `src` and returns plaintext
# Must supply `nonce`
# Must supply `additional` if supplied to #encrypt
def decrypt_string(src, dst : Bytes? = nil, *, nonce : Nonce, additional = nil) : String def decrypt_string(src, dst : Bytes? = nil, *, nonce : Nonce, additional = nil) : String
buf = decrypt src, dst, nonce: nonce, additional: additional buf = decrypt src, dst, nonce: nonce, additional: additional
# TODO: optimize # TODO: optimize
@ -67,7 +78,7 @@ module Sodium::Cipher::Aead
protected abstract def mac_size : Int32 protected abstract def mac_size : Int32
end end
{% for key, val in {"Xchacha20Poly1305Ietf" => "_xchacha20poly1305_ietf"} %} {% for key, val in {"XChaCha20Poly1305Ietf" => "_xchacha20poly1305_ietf"} %}
# Use like `SecretBox` with optional additional authenticated data. # Use like `SecretBox` with optional additional authenticated data.
# #
# See [https://libsodium.gitbook.io/doc/secret-key_cryptography/aead](https://libsodium.gitbook.io/doc/secret-key_cryptography/aead) # See [https://libsodium.gitbook.io/doc/secret-key_cryptography/aead](https://libsodium.gitbook.io/doc/secret-key_cryptography/aead)
@ -96,28 +107,30 @@ module Sodium::Cipher::Aead
ad_len = additional.try(&.bytesize) || 0 ad_len = additional.try(&.bytesize) || 0
nonce.used! nonce.used!
if LibSodium.crypto_aead{{ val.id }}_encrypt_detached(dst, mac, out mac_len, src, src.bytesize, additional, ad_len, nil, nonce.to_slice, @key.to_slice) != 0 @key.readonly do
raise Sodium::Error.new("crypto_aead_{{ val.id }}_encrypt_detached") r = LibSodium.crypto_aead{{ val.id }}_encrypt_detached(dst, mac, out mac_len, src, src.bytesize, additional, ad_len, nil, nonce.to_slice, @key.to_slice)
raise Sodium::Error.new("crypto_aead_{{ val.id }}_encrypt_detached") if r != 0
raise Sodium::Error.new("crypto_aead_{{ val.id }}_encrypt_detached mac size mismatch") if mac_len != MAC_SIZE
end end
raise Sodium::Error.new("crypto_aead_{{ val.id }}_encrypt_detached mac size mismatch") if mac_len != MAC_SIZE
{mac, dst, nonce} {mac, dst, nonce}
end end
# src and dst may be the same object but should not overlap. # `src` and `dst` may be the same object but should not overlap.
# Must supply `mac` and `nonce` # Must supply `mac` and `nonce`
# Must supply `additional` if supplied to #encrypt_detached # Must supply `additional` if supplied to #encrypt_detached
def decrypt_detached(src : Bytes, dst : Bytes? = nil, *, nonce : Sodium::Nonce, mac : Bytes, additional : String | Bytes | Nil = nil) : Bytes def decrypt_detached(src : Bytes, dst : Bytes? = nil, *, nonce : Sodium::Nonce, mac : Bytes, additional : String | Bytes | Nil = nil) : Bytes
dst ||= Bytes.new(src.bytesize) dst ||= Bytes.new src.bytesize
raise ArgumentError.new("src and dst bytesize must be identical") if src.bytesize != dst.bytesize raise ArgumentError.new("src and dst bytesize must be identical") if src.bytesize != dst.bytesize
raise ArgumentError.new("nonce size mismatch, got #{nonce.bytesize}, wanted #{NONCE_SIZE}") unless nonce.bytesize == NONCE_SIZE raise ArgumentError.new("nonce size mismatch, got #{nonce.bytesize}, wanted #{NONCE_SIZE}") unless nonce.bytesize == NONCE_SIZE
raise ArgumentError.new("mac size mismatch, got #{mac.bytesize}, wanted #{MAC_SIZE}") unless mac.bytesize == MAC_SIZE raise ArgumentError.new("mac size mismatch, got #{mac.bytesize}, wanted #{MAC_SIZE}") unless mac.bytesize == MAC_SIZE
ad_len = additional.try(&.bytesize) || 0 ad_len = additional.try(&.bytesize) || 0
if LibSodium.crypto_aead{{ val.id }}_decrypt_detached(dst, nil, src, src.bytesize, mac, additional, ad_len, nonce.to_slice, @key.to_slice) != 0 r = @key.readonly do
raise Sodium::Error::DecryptionFailed.new("crypto_aead_{{ val.id }}_decrypt_detached") LibSodium.crypto_aead{{ val.id }}_decrypt_detached(dst, nil, src, src.bytesize, mac, additional, ad_len, nonce.to_slice, @key.to_slice)
end end
raise Sodium::Error::DecryptionFailed.new("crypto_aead_{{ val.id }}_decrypt_detached") if r != 0
dst dst
end end