Add Crypto::Config

Runtime configuration of which Secret class to use
Remove Secret::Key, Secret::Large
master
Didactic Drunk 2022-05-11 22:09:27 -07:00 committed by didactic-drunk
parent 7e91cac498
commit bfbaacab4d
8 changed files with 74 additions and 68 deletions

View File

@ -1,10 +1,17 @@
require "./spec_helper" require "./spec_helper"
require "../src/crypto-secret/test" require "../src/crypto-secret/test"
require "../src/crypto-secret/not" require "../src/crypto-secret"
require "../src/crypto-secret/large" #require "../src/crypto-secret/not"
require "../src/crypto-secret/key" #require "../src/crypto-secret/bidet"
test_secret_class Crypto::Secret::Not test_secret_class Crypto::Secret::Not
test_secret_class Crypto::Secret::Bidet test_secret_class Crypto::Secret::Bidet
test_secret_class Crypto::Secret::Large
test_secret_class Crypto::Secret::Key describe Crypto::Secret do
it ".for" do
[:kgk, :key, :data, :not].each do |sym|
secret = Crypto::Secret.for sym, 2
secret.bytesize.should eq 2
end
end
end

View File

@ -3,6 +3,7 @@ require "./stateless"
module Crypto::Secret module 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. # 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
# * Not locked in memory # * Not locked in memory
# * Not access protected # * Not access protected
# * No guard pages # * No guard pages

View File

@ -0,0 +1,42 @@
{% if @type.has_constant?("Sodium") %}
CRYPTO_SECRET_KEY_CLASS = Sodium::SecureBuffer
{% else %}
CRYPTO_SECRET_KEY_CLASS = Crypto::Secret::Bidet
{% end %}
module Crypto::Secret::Config
# :nodoc:
USES = Hash(Symbol, Secret.class).new
enum SecurityLevel
Paranoid
Default
Lax
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
end
end
setup # Safe defaults
def self.register_use(klass, *uses) : Nil
uses.each do |use|
USES[use] = klass
end
end
end

View File

@ -1,16 +0,0 @@
require "./bidet"
# Use this class for holding small amounts of sensitive data such as crypto keys
#
# Underlying implentation subject to change
#
# Uses `Sodium::SecureBuffer` If "sodium" is required before "crypto-secret"
{% if @type.has_constant?("Sodium") %}
class Crypto::Secret::Key < ::Sodium::SecureBuffer
end
{% else %}
# TODO: mlock
# TODO: mprotect
class Crypto::Secret::Key < ::Crypto::Secret::Bidet
end
{% end %}

View File

@ -1,13 +0,0 @@
require "./bidet"
module Crypto::Secret
# Use this class as a default when holding possibly large amounts of data that may stress mlock limits
#
# Suitable uses: holding decrypted data
#
# no mlock
#
# Implementation subject to change
class Large < Bidet
end
end

View File

@ -33,39 +33,22 @@ module Crypto::Secret
extend ClassMethods extend ClassMethods
abstract class Base def self.new(size : Int32)
raise NotImplementedError.new("workaround for lack of `abstract def self.new`")
end end
REGISTERED = Hash(Symbol, Secret::Base.class).new def self.for(secret : Crypto::Secret) : Crypto::Secret
# REGISTERED_USES = {} of Nil => Nil secret
REGISTERED_USES = {} of Symbol => String
# REGISTERED_LOAD_PATHS = {} of Nil => Nil
REGISTERED_LOAD_PATHS = {} of String => String
macro register(klass, *args)
p "{{ args.id}}"
{% for arg in args %}
{% REGISTERED_USES[arg] = klass %}
{% end %}
end end
macro register_class(klass, load_path, *uses) def self.for(use : Symbol, size : Int32) : Crypto::Secret
{% REGISTERED_LOAD_PATHS[klass] = load_path %}
register *uses
end
register_class "Crypto::Secret::Bidet", "./bidet", :ksk, :key, :data
# register_class "Crypto::Secret::Not", "./not"
register "Crypto::Secret::Bidet", :ksk, :key, :data
def self.for(use : Symbol) : Crypto::Secret::Base.class
REGISTERED[use]
end
def self.for(use : Symbol, size : Int32)
for(use).new(size) for(use).new(size)
end end
def self.for(use : Symbol) : Crypto::Secret.class
Config::USES[use]
end
# For debugging. Leaks the secret # For debugging. Leaks the secret
# #
# Returned String **not** tracked or wiped # Returned String **not** tracked or wiped
@ -207,3 +190,4 @@ puts "key=klass"
{% end %} {% end %}
end end
require "./config"

View File

@ -31,7 +31,7 @@ module Crypto::Secret
# Marks a region allocated as readable and writable # Marks a region allocated as readable and writable
# WARNING: Not thread safe # WARNING: Not thread safe
def readwrite : self def readwrite : Secret
raise Error::KeyWiped.new if @state == State::Wiped raise Error::KeyWiped.new if @state == State::Wiped
readwrite_impl readwrite_impl
@state = State::Readwrite @state = State::Readwrite
@ -50,7 +50,7 @@ module Crypto::Secret
# Marks a region allocated using sodium_malloc() or sodium_allocarray() as read-only. # Marks a region allocated using sodium_malloc() or sodium_allocarray() as read-only.
# WARNING: Not thread safe # WARNING: Not thread safe
def readonly : self def readonly : Secret
raise Error::KeyWiped.new if @state == State::Wiped raise Error::KeyWiped.new if @state == State::Wiped
readonly_impl readonly_impl
@state = State::Readonly @state = State::Readonly
@ -59,7 +59,7 @@ module Crypto::Secret
# Makes a region inaccessible. It cannot be read or written, but the data are preserved. # Makes a region inaccessible. It cannot be read or written, but the data are preserved.
# WARNING: Not thread safe # WARNING: Not thread safe
def noaccess : self def noaccess : Secret
raise Error::KeyWiped.new if @state == State::Wiped raise Error::KeyWiped.new if @state == State::Wiped
noaccess_impl noaccess_impl
@state = State::Noaccess @state = State::Noaccess

View File

@ -2,7 +2,7 @@ require "./secret"
# Provides a 0 overhead implementation of [#readwrite, #readonly, #noaccess, #reset] with no protection # Provides a 0 overhead implementation of [#readwrite, #readonly, #noaccess, #reset] with no protection
# #
# Data is still erased when out of scope # Data is erased when #wipe is out of scope, manual #wipe, or finalized except for `Not`
module Crypto::Secret::Stateless module Crypto::Secret::Stateless
include Crypto::Secret include Crypto::Secret
@ -11,7 +11,7 @@ module Crypto::Secret::Stateless
end end
# Not thread safe # Not thread safe
def readwrite : self def readwrite : Secret
self self
end end
@ -27,7 +27,7 @@ module Crypto::Secret::Stateless
end end
# Not thread safe # Not thread safe
def readonly : self def readonly : Secret
self self
end end
@ -42,12 +42,13 @@ module Crypto::Secret::Stateless
end end
# Not thread safe # Not thread safe
def noaccess : self def noaccess : Secret
self self
end end
# Not thread safe # Not thread safe
def reset def reset : Secret
self
end end
def finalize def finalize