Document SecureBuffer thread safety
This commit is contained in:
parent
38bd985103
commit
4da95b6398
@ -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`.
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user