Sodium::Kdf keep SecureBuffer in noaccess state except when in use.
This commit is contained in:
parent
8cdb4cbf42
commit
ed7ba20082
@ -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 }
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user