From 80a47aa966f0b7febb981d389b644a16600f8181 Mon Sep 17 00:00:00 2001 From: Didactic Drunk <1479616+didactic-drunk@users.noreply.github.com> Date: Wed, 11 May 2022 22:30:05 -0700 Subject: [PATCH] Switch Crypto::Secret from module to abstract class --- README.md | 6 +++--- spec/crypto_secret_spec.cr | 2 -- src/crypto-secret/bidet.cr | 6 +++--- src/crypto-secret/config.cr | 33 ++++++++++++++++-------------- src/crypto-secret/crypto-secret.cr | 3 --- src/crypto-secret/lib.cr | 2 +- src/crypto-secret/not.cr | 6 +++--- src/crypto-secret/secret.cr | 21 ++++++++----------- src/crypto-secret/stateful.cr | 8 +------- src/crypto-secret/stateless.cr | 11 ++++------ 10 files changed, 41 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 4b6d468..3f06613 100644 --- a/README.md +++ b/README.md @@ -192,12 +192,12 @@ Each implementation may add additional protections **Only intended for use by crypto library authors** ``` -class MySecret +class MySecret < Crypto::Secret # Choose one include Crypto::Secret::Stateless include Crypto::Secret::Stateful - def initialize(size) + def initialize(size : Int32) # allocate or reference storage # optionally mlock end @@ -209,7 +209,7 @@ class MySecret # optionally reencrypt or signal HSM end - def bytesize : Int32 + def buffer_bytesize : Int32 # return the size end diff --git a/spec/crypto_secret_spec.cr b/spec/crypto_secret_spec.cr index 310702f..26bfff1 100644 --- a/spec/crypto_secret_spec.cr +++ b/spec/crypto_secret_spec.cr @@ -1,8 +1,6 @@ require "./spec_helper" require "../src/crypto-secret/test" require "../src/crypto-secret" -#require "../src/crypto-secret/not" -#require "../src/crypto-secret/bidet" test_secret_class Crypto::Secret::Not test_secret_class Crypto::Secret::Bidet diff --git a/src/crypto-secret/bidet.cr b/src/crypto-secret/bidet.cr index cd31558..167d70b 100644 --- a/src/crypto-secret/bidet.cr +++ b/src/crypto-secret/bidet.cr @@ -1,6 +1,6 @@ require "./stateless" -module Crypto::Secret +abstract class Crypto::Secret # Leaves less sh** around if you forget to wipe. A safer default for large secrets that may stress mlock limits or low confidentiality secrets. # # * Wipes on finalize but should not be relied on @@ -8,7 +8,7 @@ module Crypto::Secret # * Not access protected # * No guard pages # * Hours of fun - class Bidet < Base + class Bidet < Secret include Stateless def self.new(size : Int32) @@ -20,6 +20,6 @@ module Crypto::Secret end delegate_to_slice @bytes - delegate_to_bytesize @bytes.bytesize + delegate_buffer_bytesize_to @bytes.bytesize end end diff --git a/src/crypto-secret/config.cr b/src/crypto-secret/config.cr index b06893e..7fd2de3 100644 --- a/src/crypto-secret/config.cr +++ b/src/crypto-secret/config.cr @@ -1,3 +1,6 @@ +require "./not" +require "./bidet" + {% if @type.has_constant?("Sodium") %} CRYPTO_SECRET_KEY_CLASS = Sodium::SecureBuffer {% else %} @@ -12,23 +15,23 @@ module Crypto::Secret::Config Paranoid Default Lax - None + # None end - def self.setup(how : SecurityLevel = SecurityLevel::Default) : Nil - case how - in SecurityLevel::Paranoid - register_use Bidet, :not - register_use CRYPTO_SECRET_KEY_CLASS, :kgk, :key, :data - in SecurityLevel::Default - register_use Not, :not - register_use Bidet, :data - register_use CRYPTO_SECRET_KEY_CLASS, :kgk, :key - in SecurityLevel::Lax - register_use Not, :not - register_use Bidet, :kgk, :key, :data - in SecurityLevel::None - register_use Not, :kgk, :key, :data, :not + def self.setup(level : SecurityLevel = SecurityLevel::Default) : Nil + register_use Not, :not + + case level + in SecurityLevel::Paranoid + register_use Bidet, :not + register_use CRYPTO_SECRET_KEY_CLASS, :kgk, :key, :data + in SecurityLevel::Default + register_use Crypto::Secret::Bidet, :data + register_use CRYPTO_SECRET_KEY_CLASS, :kgk, :key + in SecurityLevel::Lax + register_use Bidet, :kgk, :key, :data + # in SecurityLevel::None + # register_use Not, :kgk, :key, :data end end diff --git a/src/crypto-secret/crypto-secret.cr b/src/crypto-secret/crypto-secret.cr index 3a0daf5..bb2e765 100644 --- a/src/crypto-secret/crypto-secret.cr +++ b/src/crypto-secret/crypto-secret.cr @@ -1,4 +1 @@ -#require "./not" require "./secret" -# require "./large" -# require "./key" diff --git a/src/crypto-secret/lib.cr b/src/crypto-secret/lib.cr index 491cbe2..d729092 100644 --- a/src/crypto-secret/lib.cr +++ b/src/crypto-secret/lib.cr @@ -1,5 +1,5 @@ module Crypto - module Secret + abstract class Secret module Stateless end diff --git a/src/crypto-secret/not.cr b/src/crypto-secret/not.cr index e770913..c42f823 100644 --- a/src/crypto-secret/not.cr +++ b/src/crypto-secret/not.cr @@ -1,6 +1,6 @@ require "./stateless" -module Crypto::Secret +abstract class Crypto::Secret # A not very secret `Secret`, but fast # # Suitable uses: @@ -12,7 +12,7 @@ module Crypto::Secret # * Not access protected # * No guard pages # * No wiping - struct Not + class Not < Secret include Stateless def self.new(size : Int32) @@ -25,7 +25,7 @@ module Crypto::Secret end delegate_to_slice @bytes - delegate_to_bytesize @bytes.bytesize + delegate_buffer_bytesize_to @bytes.bytesize def wipe end diff --git a/src/crypto-secret/secret.cr b/src/crypto-secret/secret.cr index af5562c..79eae42 100644 --- a/src/crypto-secret/secret.cr +++ b/src/crypto-secret/secret.cr @@ -10,7 +10,7 @@ require "./class_methods" # # Other shards may provide additional `Secret` types ([sodium.cr](https://github.com/didactic-drunk/sodium.cr)) @[Experimental] -module Crypto::Secret +abstract class Crypto::Secret class Error < Exception class KeyWiped < Error end @@ -162,7 +162,11 @@ module Crypto::Secret abstract def readonly(& : Bytes -> U) forall U protected abstract def to_slice(& : Bytes -> U) forall U - abstract def bytesize : Int32 + abstract def buffer_bytesize : Int32 + + def bytesize : Int32 + buffer_bytesize + end macro delegate_to_slice(to object) def to_slice(& : Bytes -> U) forall U @@ -170,8 +174,8 @@ module Crypto::Secret end end - macro delegate_to_bytesize(to object) - def bytesize : Int32 + macro delegate_buffer_bytesize_to(to object) + def buffer_bytesize : Int32 {{object.id}} end end @@ -181,13 +185,4 @@ module Crypto::Secret end end -macro finished - {% for key, klass in Crypto::Secret::REGISTERED_USES %} -#puts "{{key}}={{klass}}" -puts "key=klass" - require {{ Crypto::Secret::REGISTERED_LOAD_PATHS[klass] }} - Crypto::Secret::REGISTERED[:{{key.id}}] = {{klass.id}} - {% end %} -end - require "./config" diff --git a/src/crypto-secret/stateful.cr b/src/crypto-secret/stateful.cr index 0c31fb4..0823e4c 100644 --- a/src/crypto-secret/stateful.cr +++ b/src/crypto-secret/stateful.cr @@ -1,6 +1,6 @@ require "./secret" -module Crypto::Secret +abstract class Crypto::Secret # Development guide: # 1. Create your initialize method and optionally allocate memory # 2. Create a finalize method to deallocate memory if necessary @@ -10,12 +10,6 @@ module Crypto::Secret # # When state changes are required (such as using #noaccess) and the buffer is accessed from multiple threads wrap each #readonly/#readwrite block in a lock. module Stateful - include Crypto::Secret - - macro included - extend ClassMethods - end - @state = State::Readwrite @pre_wipe_state = State::Readwrite diff --git a/src/crypto-secret/stateless.cr b/src/crypto-secret/stateless.cr index 3879d96..2fe86ad 100644 --- a/src/crypto-secret/stateless.cr +++ b/src/crypto-secret/stateless.cr @@ -2,14 +2,11 @@ require "./secret" # Provides a 0 overhead implementation of [#readwrite, #readonly, #noaccess, #reset] with no protection # -# Data is erased when #wipe is out of scope, manual #wipe, or finalized except for `Not` +# Data is erased when (except for `Crypto::Secret::Not`): +# * #wipe(&block) goes out of scope +# * manual #wipe +# * finalized module Crypto::Secret::Stateless - include Crypto::Secret - - macro included - extend ClassMethods - end - # Not thread safe def readwrite : Secret self