From 49a999732e70109fb20574d69ccdedb489e91ca4 Mon Sep 17 00:00:00 2001 From: Didactic Drunk <1479616+didactic-drunk@users.noreply.github.com> Date: Sun, 22 May 2022 18:12:01 -0700 Subject: [PATCH] Add Crypto::Secret::Guarded Uses mmap with guard pages New SecurityLevel: :strong Adjust security levels <=> secret class mapping --- shard.yml | 5 ++++ spec/crypto_secret_spec.cr | 2 +- src/crypto-secret/config.cr | 21 ++++++++++------ src/crypto-secret/guarded.cr | 47 ++++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 src/crypto-secret/guarded.cr diff --git a/shard.yml b/shard.yml index cfd7d05..f6dabd3 100644 --- a/shard.yml +++ b/shard.yml @@ -6,4 +6,9 @@ authors: crystal: ">= 0.37" +dependencies: + mmap: + github: crystal-posix/mmap.cr + version: ">= 0.4.0" + license: MIT diff --git a/spec/crypto_secret_spec.cr b/spec/crypto_secret_spec.cr index f43af39..9f116e8 100644 --- a/spec/crypto_secret_spec.cr +++ b/spec/crypto_secret_spec.cr @@ -4,7 +4,7 @@ require "../src/crypto-secret" test_secret_class Crypto::Secret::Not test_secret_class Crypto::Secret::Bidet -# test_secret_class Crypto::Secret::Guarded +test_secret_class Crypto::Secret::Guarded describe Crypto::Secret do it ".for" do diff --git a/src/crypto-secret/config.cr b/src/crypto-secret/config.cr index 692d937..e539e38 100644 --- a/src/crypto-secret/config.cr +++ b/src/crypto-secret/config.cr @@ -1,10 +1,11 @@ require "./not" require "./bidet" +require "./guarded" {% if @type.has_constant?("Sodium") %} CRYPTO_SECRET_KEY_CLASS = Sodium::SecureBuffer {% else %} - CRYPTO_SECRET_KEY_CLASS = Crypto::Secret::Bidet + CRYPTO_SECRET_KEY_CLASS = Crypto::Secret::Guarded {% end %} module Crypto::Secret::Config @@ -12,28 +13,34 @@ module Crypto::Secret::Config USES = Hash(Symbol, Secret.class).new enum SecurityLevel + # mlocks everything (including data) Paranoid + # wipes everything + Strong + # balance between performance and wiping Default + # performance Lax # None end def self.setup(level : SecurityLevel = :default) : Nil - register_use Not, :not + register_use Not, :not, :public_key case level in SecurityLevel::Paranoid - register_use Bidet, :not, :public_key + register_use Bidet, :not + register_use Guarded, :public_key register_use CRYPTO_SECRET_KEY_CLASS, :kgk, :secret_key, :data + in SecurityLevel::Strong + register_use Bidet, :not, :public_key + register_use Crypto::Secret::Guarded, :data + register_use CRYPTO_SECRET_KEY_CLASS, :kgk, :secret_key in SecurityLevel::Default - register_use Not, :public_key register_use Crypto::Secret::Bidet, :data register_use CRYPTO_SECRET_KEY_CLASS, :kgk, :secret_key in SecurityLevel::Lax - register_use Not, :public_key register_use Bidet, :kgk, :secret_key, :data - # in SecurityLevel::None - # register_use Not, :kgk, :secret_key, :data end end diff --git a/src/crypto-secret/guarded.cr b/src/crypto-secret/guarded.cr new file mode 100644 index 0000000..6381fdf --- /dev/null +++ b/src/crypto-secret/guarded.cr @@ -0,0 +1,47 @@ +require "./stateful" +require "mmap" + +abstract class Crypto::Secret + # * Wipes on finalize but should not be relied on + # * Not locked in memory + # * Access protected + # * Guard pages + # * Won't appear in core dumps (some platforms) + class Guarded < Secret + include Stateful + + protected getter buffer_bytesize : Int32 + @dregion : Mmap::SubRegion + @data : Mmap::SubRegion + + def initialize(size : Int32) + ps = Mmap::PAGE_SIZE + pages = (size.to_f / ps).ceil + 2 + msize = pages * ps + + @buffer_bytesize = size + + @mmap = Mmap::Region.new(msize) + @mmap[0, ps].guard_page + @mmap[(pages - 1) * ps, ps].guard_page + + @dregion = @mmap[ps, (pages - 2) * ps] + @dregion.crypto_key + @data = @dregion[0, size] + end + + protected def readwrite_impl : Nil + @dregion.readwrite + end + + protected def readonly_impl : Nil + @dregion.readonly + end + + protected def noaccess_impl : Nil + @dregion.noaccess + end + + delegate_to_slice @data + end +end