Document SecureBuffer thread safety

This commit is contained in:
Didactic Drunk 2021-05-28 01:26:27 -07:00
parent 38bd985103
commit 4da95b6398
2 changed files with 18 additions and 2 deletions

View File

@ -23,7 +23,7 @@ Notes:
* Sodium::Cipher::SecretStream * Sodium::Cipher::SecretStream
* Sodium::Digest::Blake2b * Sodium::Digest::Blake2b
* Sodium::Kdf * Sodium::Kdf
* Sodium::SecureBuffer * Sodium::SecureBuffer (Half thread safe. Thread safety is documented with each method)
Notes: Notes:
* Use one instance per thread or wrap in a `Mutex`. * Use one instance per thread or wrap in a `Mutex`.

View File

@ -3,6 +3,9 @@ require "./wipe"
module Sodium module Sodium
# Allocate guarded memory using [sodium_malloc](https://libsodium.gitbook.io/doc/memory_management) # Allocate guarded memory using [sodium_malloc](https://libsodium.gitbook.io/doc/memory_management)
#
# #initialize returns readonly or readwrite for thread safety
# 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.
class SecureBuffer class SecureBuffer
class Error < Sodium::Error class Error < Sodium::Error
class KeyWiped < Error class KeyWiped < Error
@ -24,7 +27,7 @@ module Sodium
getter bytesize getter bytesize
delegate :+, :[], :[]=, to: to_slice delegate :+, :[], :[]=, :hexstring, to: to_slice
def initialize(@bytesize : Int32) def initialize(@bytesize : Int32)
@ptr = LibSodium.sodium_malloc @bytesize @ptr = LibSodium.sodium_malloc @bytesize
@ -39,6 +42,7 @@ module Sodium
# Copies bytes to a **readonly** SecureBuffer. # Copies bytes to a **readonly** SecureBuffer.
# Optionally erases bytes after copying if erase is set # Optionally erases bytes after copying if erase is set
# Returns a **readonly** SecureBuffer.
def initialize(bytes : Bytes, erase = false) def initialize(bytes : Bytes, erase = false)
initialize bytes.bytesize initialize bytes.bytesize
bytes.copy_to self.to_slice bytes.copy_to self.to_slice
@ -60,6 +64,7 @@ module Sodium
set_state sbuf.@state set_state sbuf.@state
end end
# WARNING: Not thread safe
def wipe def wipe
return if @state == State::Wiped return if @state == State::Wiped
readwrite readwrite
@ -68,18 +73,21 @@ module Sodium
noaccess! noaccess!
end end
# WARNING: Not thread safe
def wipe def wipe
yield yield
ensure ensure
wipe wipe
end end
# :nodoc:
def finalize def finalize
LibSodium.sodium_free @ptr LibSodium.sodium_free @ptr
end end
# Returns key # Returns key
# May permanently set key to readonly depending on class usage. # May permanently set key to readonly depending on class usage.
# WARNING: Not thread safe unless this object is readonly or readwrite
def to_slice : Bytes def to_slice : Bytes
case @state case @state
when State::Noaccess, State::Wiped when State::Noaccess, State::Wiped
@ -94,11 +102,13 @@ module Sodium
@ptr @ptr
end end
# WARNING: Not thread safe unless this object is readonly or readwrite
def dup def dup
self.class.new self self.class.new self
end end
# Temporarily make buffer readonly within the block returning to the prior state on exit. # 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 def readonly
with_state State::Readonly do with_state State::Readonly do
yield yield
@ -106,6 +116,7 @@ module Sodium
end end
# Temporarily make buffer readonly within the block returning to the prior state on exit. # 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 readwrite def readwrite
with_state State::Readwrite do with_state State::Readwrite do
yield yield
@ -113,6 +124,7 @@ module Sodium
end end
# Makes a region allocated using sodium_malloc() or sodium_allocarray() inaccessible. It cannot be read or written, but the data are preserved. # Makes a region allocated using sodium_malloc() or sodium_allocarray() inaccessible. It cannot be read or written, but the data are preserved.
# WARNING: Not thread safe
def noaccess def noaccess
raise Error::KeyWiped.new if @state == State::Wiped raise Error::KeyWiped.new if @state == State::Wiped
noaccess! noaccess!
@ -129,6 +141,7 @@ module Sodium
end end
# 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
def readonly def readonly
raise Error::KeyWiped.new if @state == State::Wiped raise Error::KeyWiped.new if @state == State::Wiped
if LibSodium.sodium_mprotect_readonly(@ptr) != 0 if LibSodium.sodium_mprotect_readonly(@ptr) != 0
@ -139,6 +152,7 @@ module Sodium
end end
# Marks a region allocated using sodium_malloc() or sodium_allocarray() as readable and writable, after having been protected using sodium_mprotect_readonly() or sodium_mprotect_noaccess(). # Marks a region allocated using sodium_malloc() or sodium_allocarray() as readable and writable, after having been protected using sodium_mprotect_readonly() or sodium_mprotect_noaccess().
# WARNING: Not thread safe
def readwrite def readwrite
raise Error::KeyWiped.new if @state == State::Wiped raise Error::KeyWiped.new if @state == State::Wiped
if LibSodium.sodium_mprotect_readwrite(@ptr) != 0 if LibSodium.sodium_mprotect_readwrite(@ptr) != 0
@ -158,6 +172,7 @@ module Sodium
Sodium.memcmp self.to_slice, other Sodium.memcmp self.to_slice, other
end end
# WARNING: Not thread safe
private def set_state(new_state : State) private def set_state(new_state : State)
return if @state == new_state return if @state == new_state
@ -171,6 +186,7 @@ module Sodium
end end
end end
# WARNING: Only thread safe when current state >= requested state
private def with_state(new_state : State) private def with_state(new_state : State)
old_state = @state old_state = @state
# Only change when new_state needs more access than @state. # Only change when new_state needs more access than @state.