Sodium::Kdf keep SecureBuffer in noaccess state except when in use.

This commit is contained in:
Didactic Drunk 2019-09-01 10:31:36 -07:00
parent 8cdb4cbf42
commit ed7ba20082
4 changed files with 46 additions and 14 deletions

View File

@ -1,6 +1,9 @@
require "../spec_helper" require "../spec_helper"
require "../../src/sodium/secure_buffer" require "../../src/sodium/secure_buffer"
class FakeError < Exception
end
describe Sodium::SecureBuffer do describe Sodium::SecureBuffer do
it "allocates empty" do it "allocates empty" do
buf = Sodium::SecureBuffer.new 5 buf = Sodium::SecureBuffer.new 5
@ -58,11 +61,22 @@ describe Sodium::SecureBuffer do
buf.readwrite buf.readwrite
buf.@state.should eq Sodium::SecureBuffer::State::Readwrite buf.@state.should eq Sodium::SecureBuffer::State::Readwrite
buf.readonly { }
buf.@state.should eq Sodium::SecureBuffer::State::Readwrite
buf.wipe buf.wipe
buf.@state.should eq Sodium::SecureBuffer::State::Wiped buf.@state.should eq Sodium::SecureBuffer::State::Wiped
end end
it "temporarily transitions correctly with exceptions" do
buf = Sodium::SecureBuffer.new(5).noaccess
begin
buf.readonly { raise FakeError.new }
rescue FakeError
end
buf.@state.should eq Sodium::SecureBuffer::State::Noaccess
end
it "can wipe more than once" do it "can wipe more than once" do
buf = Sodium::SecureBuffer.new 5 buf = Sodium::SecureBuffer.new 5
3.times { buf.wipe } 3.times { buf.wipe }

View File

@ -11,6 +11,10 @@ module Sodium
# subkey_id = 0 # subkey_id = 0
# output_size = 16 # output_size = 16
# subkey = kdf.derive "8bytectx", subkey_id, output_size # subkey = kdf.derive "8bytectx", subkey_id, output_size
#
# Memory for this class is held in a sodium guarded page with noaccess.
# Readonly access is temporarily enabled when deriving keys.
# Calling #to_slice marks the page readonly permanently.
# ``` # ```
class Kdf class Kdf
include Wipe include Wipe
@ -30,7 +34,7 @@ module Sodium
raise ArgumentError.new("bytes must be #{KEY_SIZE}, got #{bytes.bytesize}") raise ArgumentError.new("bytes must be #{KEY_SIZE}, got #{bytes.bytesize}")
end end
@sbuf = SecureBuffer.new bytes, erase @sbuf = SecureBuffer.new(bytes, erase).noaccess
end end
# Use an existing KDF SecureBuffer key. # Use an existing KDF SecureBuffer key.
@ -38,14 +42,14 @@ module Sodium
if @sbuf.bytesize != KEY_SIZE if @sbuf.bytesize != KEY_SIZE
raise ArgumentError.new("bytes must be #{KEY_SIZE}, got #{sbuf.bytesize}") raise ArgumentError.new("bytes must be #{KEY_SIZE}, got #{sbuf.bytesize}")
end end
@sbuf.readonly @sbuf.noaccess
end end
# Generate a new random KDF key. # Generate a new random KDF key.
# #
# Make sure to save kdf.to_slice before kdf goes out of scope. # Make sure to save kdf.to_slice before kdf goes out of scope.
def initialize def initialize
@sbuf = SecureBuffer.random KEY_SIZE @sbuf = SecureBuffer.random(KEY_SIZE).noaccess
end end
# Derive a consistent subkey based on `context` and `subkey_id`. # Derive a consistent subkey based on `context` and `subkey_id`.
@ -62,9 +66,12 @@ module Sodium
end end
subkey = SecureBuffer.new subkey_size subkey = SecureBuffer.new subkey_size
if (ret = LibSodium.crypto_kdf_derive_from_key(subkey, subkey.bytesize, subkey_id, context, self.to_slice)) != 0 @sbuf.readonly do
raise Sodium::Error.new("crypto_kdf_derive_from_key returned #{ret} (subkey size is probably out of range)") if (ret = LibSodium.crypto_kdf_derive_from_key(subkey, subkey.bytesize, subkey_id, context, self.to_slice)) != 0
raise Sodium::Error.new("crypto_kdf_derive_from_key returned #{ret} (subkey size is probably out of range)")
end
end end
subkey subkey
end end

View File

@ -327,7 +327,11 @@ module Sodium
true true
end end
def self.memzero(bytes : Bytes) def self.memzero(bytes : Bytes) : Nil
LibSodium.sodium_memzero bytes, bytes.bytesize LibSodium.sodium_memzero bytes, bytes.bytesize
end end
def self.memzero(str : String) : Nil
memzero str.to_slice
end
end end

View File

@ -13,10 +13,10 @@ module Sodium
end end
enum State enum State
Readwrite
Readonly
Noaccess
Wiped Wiped
Noaccess
Readonly
Readwrite
end end
@state = State::Readwrite @state = State::Readwrite
@ -154,11 +154,18 @@ 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.
set_state new_state if new_state >= @state
yield yield
ensure else
set_state old_state if old_state begin
old_state = @state
set_state new_state
yield
ensure
set_state old_state if old_state
end
end
end end
end end
end end