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

master
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 "../../src/sodium/secure_buffer"
class FakeError < Exception
end
describe Sodium::SecureBuffer do
it "allocates empty" do
buf = Sodium::SecureBuffer.new 5
@ -58,11 +61,22 @@ describe Sodium::SecureBuffer do
buf.readwrite
buf.@state.should eq Sodium::SecureBuffer::State::Readwrite
buf.readonly { }
buf.@state.should eq Sodium::SecureBuffer::State::Readwrite
buf.wipe
buf.@state.should eq Sodium::SecureBuffer::State::Wiped
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
buf = Sodium::SecureBuffer.new 5
3.times { buf.wipe }

View File

@ -11,6 +11,10 @@ module Sodium
# subkey_id = 0
# output_size = 16
# 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
include Wipe
@ -30,7 +34,7 @@ module Sodium
raise ArgumentError.new("bytes must be #{KEY_SIZE}, got #{bytes.bytesize}")
end
@sbuf = SecureBuffer.new bytes, erase
@sbuf = SecureBuffer.new(bytes, erase).noaccess
end
# Use an existing KDF SecureBuffer key.
@ -38,14 +42,14 @@ module Sodium
if @sbuf.bytesize != KEY_SIZE
raise ArgumentError.new("bytes must be #{KEY_SIZE}, got #{sbuf.bytesize}")
end
@sbuf.readonly
@sbuf.noaccess
end
# Generate a new random KDF key.
#
# Make sure to save kdf.to_slice before kdf goes out of scope.
def initialize
@sbuf = SecureBuffer.random KEY_SIZE
@sbuf = SecureBuffer.random(KEY_SIZE).noaccess
end
# Derive a consistent subkey based on `context` and `subkey_id`.
@ -62,9 +66,12 @@ module Sodium
end
subkey = SecureBuffer.new subkey_size
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)")
@sbuf.readonly do
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
subkey
end

View File

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

View File

@ -13,10 +13,10 @@ module Sodium
end
enum State
Readwrite
Readonly
Noaccess
Wiped
Noaccess
Readonly
Readwrite
end
@state = State::Readwrite
@ -154,11 +154,18 @@ module Sodium
end
private def with_state(new_state : State)
old_state = @state
set_state new_state
yield
ensure
set_state old_state if old_state
# Only change when new_state needs more access than @state.
if new_state >= @state
yield
else
begin
old_state = @state
set_state new_state
yield
ensure
set_state old_state if old_state
end
end
end
end
end