From bd5e89dcd4d81af497e836c3269bae8653ce4d5a Mon Sep 17 00:00:00 2001 From: Didactic Drunk <1479616+didactic-drunk@users.noreply.github.com> Date: Wed, 16 Jun 2021 18:19:30 -0700 Subject: [PATCH] Internal switch to Crypto::Secret --- spec/sodium/cipher/aead/chalsa_spec.cr | 15 ++++-- spec/sodium/cipher/chalsa_spec.cr | 10 +++- spec/sodium/crypto_box/secret_key_spec.cr | 8 ++- src/sodium/cipher/chalsa.cr | 8 +-- src/sodium/crypto_box/secret_key.cr | 65 ++++++++++++++--------- src/sodium/digest/blake2b.cr | 14 +++-- 6 files changed, 78 insertions(+), 42 deletions(-) diff --git a/spec/sodium/cipher/aead/chalsa_spec.cr b/spec/sodium/cipher/aead/chalsa_spec.cr index c66cc6a..811ad9d 100644 --- a/spec/sodium/cipher/aead/chalsa_spec.cr +++ b/spec/sodium/cipher/aead/chalsa_spec.cr @@ -89,15 +89,20 @@ end end it "dups" do - box1 = Sodium::Cipher::Aead::{{ name.id }}.new Bytes.new(Sodium::Cipher::Aead::{{ name.id }}::KEY_SIZE) + key = Bytes.new Sodium::Cipher::Aead::{{ name.id }}::KEY_SIZE + box1 = Sodium::Cipher::Aead::{{ name.id }}.new key box2 = box1.dup key1 = box1.key key2 = box2.key - key2.readwrite - - key2.to_slice[0] = 1_u8 - key1.to_slice[0].should eq 0_u8 + key1.should eq key2 + key2.readwrite do |ks| + ks[0] = 1_u8 + end + key1.readonly do |ks| + ks[0].should eq 0_u8 + end + key1.should_not eq key2 end end diff --git a/spec/sodium/cipher/chalsa_spec.cr b/spec/sodium/cipher/chalsa_spec.cr index d48f1ae..525f656 100644 --- a/spec/sodium/cipher/chalsa_spec.cr +++ b/spec/sodium/cipher/chalsa_spec.cr @@ -26,8 +26,14 @@ require "../../../src/sodium/cipher/chalsa" key1 = cipher1.key key2 = cipher2.key - key2.to_slice[0] = 1_u8 - key1.to_slice[0].should eq 0_u8 + key1.should eq key2 + key2.readwrite do |ks| + ks[0] = 1_u8 + end + key1.readonly do |ks| + ks[0].should eq 0_u8 + end + key1.should_not eq key2 end end {% end %} diff --git a/spec/sodium/crypto_box/secret_key_spec.cr b/spec/sodium/crypto_box/secret_key_spec.cr index 743c57a..2baa32d 100644 --- a/spec/sodium/crypto_box/secret_key_spec.cr +++ b/spec/sodium/crypto_box/secret_key_spec.cr @@ -34,14 +34,12 @@ private def box_from_vec(vec) end end -private def new_key_bytes - Sodium::CryptoBox::SecretKey.new.to_slice -end - describe Sodium::CryptoBox::SecretKey do it "loads keys" do key1 = Sodium::CryptoBox::SecretKey.new - key2 = Sodium::CryptoBox::SecretKey.new key1.to_slice, key1.public_key.to_slice + key2 = key1.key.readonly do |ks| + Sodium::CryptoBox::SecretKey.new ks, key1.public_key.to_slice + end key1.to_slice.should eq key2.to_slice key1.public_key.to_slice.should eq key2.public_key.to_slice end diff --git a/src/sodium/cipher/chalsa.cr b/src/sodium/cipher/chalsa.cr index ee0ac61..bd76005 100644 --- a/src/sodium/cipher/chalsa.cr +++ b/src/sodium/cipher/chalsa.cr @@ -15,7 +15,6 @@ module Sodium::Cipher def initialize(key : Crypto::Secret | Bytes, nonce = nil) raise ArgumentError.new("key must be #{key_size} bytes, got #{key.bytesize}") if key.bytesize != key_size @key = key.is_a?(Crypto::Secret) ? key : Sodium::SecureBuffer.new(key) -# self.key = key if key self.nonce = nonce if nonce end @@ -116,8 +115,11 @@ module Sodium::Cipher def update(src : Bytes, dst : Bytes) : Bytes 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, n, @offset, k.to_slice) != 0 - raise Sodium::Error.new("crypto_stream_{{ val.id }}_xor_ic") + + k.readonly do |kslice| + if LibSodium.crypto_stream_{{ val.id }}_xor_ic(dst, src, src.bytesize, n, @offset, kslice) != 0 + raise Sodium::Error.new("crypto_stream_{{ val.id }}_xor_ic") + end end @offset += src.bytesize dst diff --git a/src/sodium/crypto_box/secret_key.cr b/src/sodium/crypto_box/secret_key.cr index e7f633c..398e1c0 100644 --- a/src/sodium/crypto_box/secret_key.cr +++ b/src/sodium/crypto_box/secret_key.cr @@ -39,19 +39,22 @@ class Sodium::CryptoBox SEED_SIZE = LibSodium.crypto_box_seedbytes.to_i SEAL_SIZE = LibSodium.crypto_box_sealbytes.to_i + getter key : Crypto::Secret getter public_key : PublicKey # Returns key - delegate_to_slice to: @sbuf + delegate_to_slice to: @key - @seed : SecureBuffer? + @seed : Crypto::Secret? # Generate a new random secret/public key pair. def initialize - @sbuf = SecureBuffer.new KEY_SIZE + @key = SecureBuffer.new KEY_SIZE @public_key = PublicKey.new - if LibSodium.crypto_box_keypair(@public_key.to_slice, self.to_slice) != 0 - raise Sodium::Error.new("crypto_box_keypair") + @key.readwrite do |kslice| + if LibSodium.crypto_box_keypair(@public_key.to_slice, kslice) != 0 + raise Sodium::Error.new("crypto_box_keypair") + end end end @@ -59,14 +62,16 @@ class Sodium::CryptoBox # # Takes ownership of an existing key in a SecureBuffer. # Recomputes the public key from a secret key if missing. - def initialize(@sbuf : SecureBuffer, pkey : Bytes? = nil) - raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{@sbuf.bytesize}") if @sbuf.bytesize != KEY_SIZE + def initialize(@key : Crypto::Secret, pkey : Bytes? = nil) + raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{@key.bytesize}") if @key.bytesize != KEY_SIZE if pk = pkey @public_key = PublicKey.new pk else @public_key = PublicKey.new - if LibSodium.crypto_scalarmult_base(@public_key.to_slice, self.to_slice) != 0 - raise Sodium::Error.new("crypto_scalarmult_base") + @key.readonly do |kslice| + if LibSodium.crypto_scalarmult_base(@public_key.to_slice, kslice) != 0 + raise Sodium::Error.new("crypto_scalarmult_base") + end end end end @@ -77,13 +82,15 @@ class Sodium::CryptoBox # Recomputes the public key from a secret key if missing. def initialize(bytes : Bytes, pkey : Bytes? = nil) raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}") if bytes.bytesize != KEY_SIZE - @sbuf = SecureBuffer.new bytes + @key = SecureBuffer.new bytes if pk = pkey @public_key = PublicKey.new pk else @public_key = PublicKey.new - if LibSodium.crypto_scalarmult_base(@public_key.to_slice, self.to_slice) != 0 - raise Sodium::Error.new("crypto_scalarmult_base") + @key.readonly do |kslice| + if LibSodium.crypto_scalarmult_base(@public_key.to_slice, kslice) != 0 + raise Sodium::Error.new("crypto_scalarmult_base") + end end end end @@ -93,30 +100,38 @@ class Sodium::CryptoBox # Copies seed to a SecureBuffer. def initialize(*, seed : Bytes, erase = false) raise ArgumentError.new("Secret sign seed must be #{SEED_SIZE}, got #{seed.bytesize}") unless seed.bytesize == SEED_SIZE - @seed = SecureBuffer.new seed, erase: erase + @seed = seed = SecureBuffer.new seed, erase: erase - @sbuf = SecureBuffer.new KEY_SIZE + @key = SecureBuffer.new KEY_SIZE @public_key = PublicKey.new - if LibSodium.crypto_box_seed_keypair(@public_key.to_slice, self.to_slice, seed) != 0 - raise Sodium::Error.new("crypto_box_seed_keypair") + seed.readonly do |seed_slice| + @key.readwrite do |kslice| + if LibSodium.crypto_box_seed_keypair(@public_key.to_slice, kslice, seed_slice) != 0 + raise Sodium::Error.new("crypto_box_seed_keypair") + end + end end end # Derive a new secret/public key pair based on a consistent seed. - def initialize(*, seed : SecureBuffer) + def initialize(*, seed : Crypto::Secret) raise ArgumentError.new("Secret sign seed must be #{SEED_SIZE}, got #{seed.bytesize}") unless seed.bytesize == SEED_SIZE @seed = seed - @sbuf = SecureBuffer.new KEY_SIZE + @key = SecureBuffer.new KEY_SIZE @public_key = PublicKey.new - if LibSodium.crypto_box_seed_keypair(@public_key.to_slice, self.to_slice, seed) != 0 - raise Sodium::Error.new("crypto_box_seed_keypair") + seed.readonly do |seed_slice| + @key.readwrite do |kslice| + if LibSodium.crypto_box_seed_keypair(@public_key.to_slice, kslice, seed_slice) != 0 + raise Sodium::Error.new("crypto_box_seed_keypair") + end + end end end - def seed + def seed : Crypto::Secret # BUG: Generate seed if not set. - @seed.not_nil!.to_slice + @seed.not_nil! end # Return a Box containing a precomputed shared secret for use with authenticated encryption/decryption. @@ -159,8 +174,10 @@ class Sodium::CryptoBox dst ||= Bytes.new dst_size raise ArgumentError.new("dst.bytesize must be src.bytesize - SEAL_SIZE, got #{dst.bytesize}") unless dst.bytesize == dst_size - if LibSodium.crypto_box_seal_open(dst, src, src.bytesize, @public_key.to_slice, self.to_slice) != 0 - raise Sodium::Error.new("crypto_box_seal_open") + @key.readonly do |kslice| + if LibSodium.crypto_box_seal_open(dst, src, src.bytesize, @public_key.to_slice, kslice) != 0 + raise Sodium::Error.new("crypto_box_seal_open") + end end dst end diff --git a/src/sodium/digest/blake2b.cr b/src/sodium/digest/blake2b.cr index 6f0bfd7..4cd05af 100644 --- a/src/sodium/digest/blake2b.cr +++ b/src/sodium/digest/blake2b.cr @@ -62,15 +62,23 @@ module Sodium::Digest # # `key`, `salt`, and `personal` are all optional. Many other libsodium bindings don't support them. # Check the other implementation(s) you need to interoperate with before using. - def initialize(@digest_size : Int32 = OUT_SIZE, key : Bytes? | SecureBuffer? = nil, salt : Bytes? = nil, personal : Bytes? = nil) + def initialize(@digest_size : Int32 = OUT_SIZE, key : Bytes? | Crypto::Secret? = nil, salt : Bytes? = nil, personal : Bytes? = nil) if (k = key) && k.bytesize > 0 - k = k.to_slice raise ArgumentError.new("key larger than KEY_SIZE_MAX(#{KEY_SIZE_MAX}), got #{k.bytesize}") if k.bytesize > KEY_SIZE_MAX # Test vectors contain small key sizes. Small keys shouldn't be used... Wtf? Log.warn &.emit("key smaller than KEY_SIZE_MIN(#{KEY_SIZE_MIN}), got #{k.bytesize}") if k.bytesize < KEY_SIZE_MIN # raise ArgumentError.new("key smaller than KEY_SIZE_MIN(#{KEY_SIZE_MIN}), got #{k.bytesize}") if k.bytesize < KEY_SIZE_MIN + + case k + in Bytes + k.copy_to @key.to_slice + in Crypto::Secret + k.readonly do |k_slice| + k_slice.copy_to @key.to_slice + end + end + @key_size = k.bytesize - k.copy_to @key.to_slice end if sa = salt