Fix Sodium::SecureBuffer state transitions.

This commit is contained in:
Didactic Drunk 2019-09-13 21:33:21 -07:00
parent 964fe714e9
commit b212f6bacd
3 changed files with 25 additions and 18 deletions

View File

@ -51,17 +51,17 @@ describe Sodium::SecureBuffer do
buf.noaccess buf.noaccess
buf.@state.should eq Sodium::SecureBuffer::State::Noaccess buf.@state.should eq Sodium::SecureBuffer::State::Noaccess
buf.readonly { } buf.readonly { buf.@state.should eq Sodium::SecureBuffer::State::Readonly }
buf.@state.should eq Sodium::SecureBuffer::State::Noaccess buf.@state.should eq Sodium::SecureBuffer::State::Noaccess
buf.readonly buf.readonly
buf.@state.should eq Sodium::SecureBuffer::State::Readonly buf.@state.should eq Sodium::SecureBuffer::State::Readonly
buf.readwrite { } buf.readwrite { buf.@state.should eq Sodium::SecureBuffer::State::Readwrite }
buf.@state.should eq Sodium::SecureBuffer::State::Readonly buf.@state.should eq Sodium::SecureBuffer::State::Readonly
buf.readwrite buf.readwrite
buf.@state.should eq Sodium::SecureBuffer::State::Readwrite buf.@state.should eq Sodium::SecureBuffer::State::Readwrite
buf.readonly { } buf.readonly { buf.@state.should eq Sodium::SecureBuffer::State::Readwrite }
buf.@state.should eq Sodium::SecureBuffer::State::Readwrite buf.@state.should eq Sodium::SecureBuffer::State::Readwrite
buf.wipe buf.wipe

View File

@ -21,19 +21,19 @@ module Sodium
MAC_SIZE = LibSodium.crypto_secretbox_macbytes.to_i MAC_SIZE = LibSodium.crypto_secretbox_macbytes.to_i
# Returns key # Returns key
delegate to_slice, to: @buf delegate to_slice, to: @key
# Generate a new random key held in a SecureBuffer. # Generate a new random key held in a SecureBuffer.
def initialize def initialize
@buf = SecureBuffer.random KEY_SIZE @key = SecureBuffer.random KEY_SIZE
end end
# Use an existing SecureBuffer. # Use an existing SecureBuffer.
def initialize(@buf : SecureBuffer) def initialize(@key : SecureBuffer)
if @buf.bytesize != KEY_SIZE if @key.bytesize != KEY_SIZE
raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{@buf.bytesize}") raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{@key.bytesize}")
end end
@buf.readonly @key.readonly
end end
# Copy bytes to a new SecureBuffer # Copy bytes to a new SecureBuffer
@ -43,7 +43,7 @@ module Sodium
if bytes.bytesize != KEY_SIZE if bytes.bytesize != KEY_SIZE
raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}") raise ArgumentError.new("Secret key must be #{KEY_SIZE} bytes, got #{bytes.bytesize}")
end end
@buf = SecureBuffer.new bytes, erase: erase @key = SecureBuffer.new bytes, erase: erase
end end
# Encrypts data and returns {ciphertext, nonce} # Encrypts data and returns {ciphertext, nonce}
@ -59,9 +59,10 @@ module Sodium
raise ArgumentError.new("dst.bytesize must be src.bytesize + MAC_SIZE, got #{dst.bytesize}") raise ArgumentError.new("dst.bytesize must be src.bytesize + MAC_SIZE, got #{dst.bytesize}")
end end
nonce.used! nonce.used!
if LibSodium.crypto_secretbox_easy(dst, src, src.bytesize, nonce.to_slice, self.to_slice) != 0 r = @key.readonly do
raise Sodium::Error.new("crypto_secretbox_easy") LibSodium.crypto_secretbox_easy(dst, src, src.bytesize, nonce.to_slice, @key)
end end
raise Sodium::Error.new("crypto_secretbox_easy") if r != 0
{dst, nonce} {dst, nonce}
end end
@ -80,9 +81,10 @@ module Sodium
if dst.bytesize != (src.bytesize - MAC_SIZE) if dst.bytesize != (src.bytesize - MAC_SIZE)
raise ArgumentError.new("dst.bytesize must be src.bytesize - MAC_SIZE, got #{dst.bytesize}") raise ArgumentError.new("dst.bytesize must be src.bytesize - MAC_SIZE, got #{dst.bytesize}")
end end
if LibSodium.crypto_secretbox_open_easy(dst, src, src.bytesize, nonce.to_slice, self.to_slice) != 0 r = @key.readonly do
raise Sodium::Error::DecryptionFailed.new("crypto_secretbox_easy") LibSodium.crypto_secretbox_open_easy(dst, src, src.bytesize, nonce.to_slice, @key)
end end
raise Sodium::Error::DecryptionFailed.new("crypto_secretbox_easy") if r != 0
dst dst
end end

View File

@ -68,7 +68,12 @@ module Sodium
# Returns key # Returns key
# May permanently set key to readonly depending on class usage. # May permanently set key to readonly depending on class usage.
def to_slice def to_slice
readonly if @state == State::Noaccess case @state
when State::Noaccess, State::Wiped
readonly
else
# Ok
end
Slice(UInt8).new @ptr, @bytesize Slice(UInt8).new @ptr, @bytesize
end end
@ -154,16 +159,16 @@ module Sodium
end end
private def with_state(new_state : State) private def with_state(new_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.
if new_state >= @state if old_state >= new_state
yield yield
else else
begin begin
old_state = @state
set_state new_state set_state new_state
yield yield
ensure ensure
set_state old_state if old_state set_state old_state
end end
end end
end end