Add Crypto::Secret::Stateful
This commit is contained in:
parent
c3829d4ae6
commit
1008e68035
108
src/crypto-secret/stateful.cr
Normal file
108
src/crypto-secret/stateful.cr
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
require "./secret"
|
||||||
|
|
||||||
|
module Crypto::Secret
|
||||||
|
# Development guide:
|
||||||
|
# 1. Create your initialize method and optionally allocate memory
|
||||||
|
# 2. Create a finalize method to deallocate memory if necessary
|
||||||
|
# 3. Fill in the missing abstract methods
|
||||||
|
# 4. Optionally override any included methods (especially wipe_impl if the secret is not held in the provided slice)
|
||||||
|
# 5. Provide and test a dup method or raise on dup if not possible
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
|
@state = State::Readwrite
|
||||||
|
|
||||||
|
# Temporarily make buffer readwrite within the block returning to the prior state on exit.
|
||||||
|
# WARNING: Not thread safe unless this object is **readwrite**
|
||||||
|
def readwrite
|
||||||
|
with_state State::Readwrite do
|
||||||
|
to_slice do |slice|
|
||||||
|
yield slice
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Marks a region allocated as readable and writable
|
||||||
|
# WARNING: Not thread safe
|
||||||
|
def readwrite
|
||||||
|
raise Error::KeyWiped.new if @state == State::Wiped
|
||||||
|
readwrite_impl
|
||||||
|
@state = State::Readwrite
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
# Temporarily make buffer readonly within the block returning to the prior state on exit.
|
||||||
|
# WARNING: Not thread safe unless this object is readonly or readwrite
|
||||||
|
def readonly
|
||||||
|
with_state State::Readonly do
|
||||||
|
to_slice do |slice|
|
||||||
|
yield slice
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Marks a region allocated using sodium_malloc() or sodium_allocarray() as read-only.
|
||||||
|
# WARNING: Not thread safe
|
||||||
|
def readonly
|
||||||
|
raise Error::KeyWiped.new if @state == State::Wiped
|
||||||
|
readonly_impl
|
||||||
|
@state = State::Readonly
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
# Makes a region inaccessible. It cannot be read or written, but the data are preserved.
|
||||||
|
# WARNING: Not thread safe
|
||||||
|
def noaccess
|
||||||
|
raise Error::KeyWiped.new if @state == State::Wiped
|
||||||
|
noaccess_impl
|
||||||
|
@state = State::Noaccess
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
# WARNING: Not thread safe
|
||||||
|
private def set_state(new_state : State)
|
||||||
|
return if @state == new_state
|
||||||
|
|
||||||
|
case new_state
|
||||||
|
when State::Readwrite; readwrite
|
||||||
|
when State::Readonly ; readonly
|
||||||
|
when State::Noaccess ; noaccess
|
||||||
|
when State::Wiped ; raise Error::InvalidStateTransition.new
|
||||||
|
else
|
||||||
|
raise "unknown state #{new_state}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# WARNING: Only thread safe when current state >= requested state
|
||||||
|
private def with_state(new_state : State)
|
||||||
|
old_state = @state
|
||||||
|
# Only change when new_state needs more access than @state.
|
||||||
|
if old_state >= new_state
|
||||||
|
yield
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
set_state new_state
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
set_state old_state
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# WARNING: Not thread safe
|
||||||
|
def wipe
|
||||||
|
return if @state == State::Wiped
|
||||||
|
readwrite do |slice|
|
||||||
|
wipe_impl slice
|
||||||
|
end
|
||||||
|
noaccess_impl
|
||||||
|
@state = State::Wiped
|
||||||
|
end
|
||||||
|
|
||||||
|
protected abstract def readwrite_impl : Nil
|
||||||
|
protected abstract def readonly_impl : Nil
|
||||||
|
protected abstract def noaccess_impl : Nil
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user