From 65ad5987d4399e2ece85746eb32b4a93f682e7ea Mon Sep 17 00:00:00 2001 From: Didactic Drunk <1479616+didactic-drunk@users.noreply.github.com> Date: Mon, 8 Jul 2019 16:08:39 -0700 Subject: [PATCH] SecureBuffer for Chalsa. --- spec/sodium/cipher/chalsa_spec.cr | 11 +++++++++-- src/sodium/cipher/chalsa.cr | 27 ++++++++++++--------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/spec/sodium/cipher/chalsa_spec.cr b/spec/sodium/cipher/chalsa_spec.cr index fc94568..cd296a8 100644 --- a/spec/sodium/cipher/chalsa_spec.cr +++ b/spec/sodium/cipher/chalsa_spec.cr @@ -6,17 +6,24 @@ require "../../../src/sodium/cipher/chalsa" describe Sodium::Cipher::{{ name.id }} do it "xors" do data = Bytes.new(100) + cipher1 = Sodium::Cipher::{{ name.id }}.new cipher2 = Sodium::Cipher::{{ name.id }}.new + key = cipher1.random_key + cipher2.key = key + nonce = cipher1.random_nonce + cipher2.nonce = nonce + + output = cipher1.update data + output.should_not eq data # Verify encryption did something. cipher1.update(data).should_not eq output # Verify offset is incremented. cipher1.final.should eq Bytes.new(0) - cipher2.key = key - cipher2.nonce = nonce cipher2.update(output).should eq data + cipher2.final.should eq Bytes.new(0) end end {% end %} diff --git a/src/sodium/cipher/chalsa.cr b/src/sodium/cipher/chalsa.cr index 74954be..6f5754b 100644 --- a/src/sodium/cipher/chalsa.cr +++ b/src/sodium/cipher/chalsa.cr @@ -1,17 +1,12 @@ require "../lib_sodium" -require "../wipe" +require "../secure_buffer" module Sodium::Cipher # The great beat you can eat! # # What? They're both dance? - # - # WARNING: This class takes ownership of any key material passed to it. - # - # WARNING: Not validated against test vectors. You should probably write some before using. abstract class Chalsa - @[Wipe::Var] - @key : Bytes? + @key : Bytes | SecureBuffer | Nil @nonce : Bytes? # Advanced usage. Don't touch. @@ -25,7 +20,7 @@ module Sodium::Cipher self.nonce = nonce if nonce end - def key=(key : Bytes) + def key=(key : Bytes | SecureBuffer) raise ArgumentError.new("key must be #{key_size} bytes, got #{key.bytesize}") if key.bytesize != key_size @key = key key @@ -38,7 +33,7 @@ module Sodium::Cipher end def random_key - self.key = Random::Secure.random_bytes key_size + self.key = SecureBuffer.random key_size end def random_nonce @@ -50,13 +45,13 @@ module Sodium::Cipher update src, Bytes.new(src.bytesize) end - # Provided for compatibility with block ciphers. + # Provided for compatibility with block or tagged ciphers. # Stream ciphers don't have additional data. def final Bytes.new(0) end - # Sadness... + # Always returns false. Sadness... def edible? false end @@ -76,14 +71,16 @@ module Sodium::Cipher # This class mimicks the OpenSSL::Cipher interface with minor differences. # # See `spec/sodium/cipher/chalsa_spec.cr` for examples on how to use this class. + # + # WARNING: Not validated against test vectors. You should probably write some before using this class. class {{ key.id }} < Chalsa # Xor's src with the cipher output and places in dst. # # src and dst may be the same object but should not overlap. def update(src : Bytes, dst : Bytes) : Bytes - if (key = @key) && (nonce = @nonce) + if (k = @key) && (n = @nonce) raise ArgumentError.new("src and dst bytesize must be identical") if src.bytesize != dst.bytesize - if LibSodium.crypto_stream_{{ val.id }}_xor_ic(dst, src, src.bytesize, nonce, @offset, key) != 0 + if LibSodium.crypto_stream_{{ val.id }}_xor_ic(dst, src, src.bytesize, n, @offset, k.to_slice) != 0 raise Sodium::Error.new("crypto_stream_{{ val.id }}_xor_ic") end @offset += src.bytesize @@ -94,11 +91,11 @@ module Sodium::Cipher end def key_size - LibSodium.crypto_stream_chacha20_ietf_keybytes + LibSodium.crypto_stream_chacha20_ietf_keybytes.to_i32 end def nonce_size - LibSodium.crypto_stream_chacha20_ietf_noncebytes + LibSodium.crypto_stream_chacha20_ietf_noncebytes.to_i32 end end {% end %}