[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Secure tables in Lua: Summary
- From: "Dr. Markus Walther" <walther@...>
- Date: Thu, 03 Jul 2008 15:08:12 +0200
Thanks for all the helpful feedback and example code!
The consensus seems to be that this can be done in pure Lua.
I note that both David Given's solution and Doug Roger's improvement
allow violating the integrity of the secure table:
t.newkey = nil
or similar works.
According to my goal stated in the earlier posting
"the goal is to create secure abstract data types (stacks,
etc.) that protect suitable parts of their internal representations
(e.g. revealing only access methods)."
I would also like to prefill the to-be-secured table with methods
working on the abstract data type.
Starting from the existing proposals and adding these slight
improvements I offer my own solution below.
Open issues:
- is this efficient enough for production use (cost of function calls
and metatable accesses) - or is a low-level implementation still called for?
- is it possible to see the prefilled methods without compromising their
read-only nature (needed for automatic method discovery)?
Again, thanks to all who responded!
--Markus
function create_secure_ADT(masterKey,data)
data = data or {}
assert(type(data) == "table","'data' parameter must be of type 'table'")
local key, lastKey, canAddNewKeys = {}, nil, true
local mt = {
__index = data,
__newindex =
function(t, k, v)
if k == "newkey" and canAddNewKeys then
-- if t.newkey = nil with
-- semantics 'disable newKey functionality'
-- not needed (or nil can be legal value),
-- delete next 4 lines
if v == nil then
canAddNewKeys = false
return
end
k = {}
key[k] = true
lastKey = k
end
if key[k] then
data[k] = v
return
end
error "use t.newkey = value"
end
}
local function unlock(key)
return key == masterKey and
function (self) return self[lastKey] end or nil
end
return setmetatable({},mt), unlock
end
if UNIT_TEST then
-- a single-instance stack A(bstract) D(ata) T(ype)
local stackADT = {
push = function (self, value, unlock)
local hiddenData = unlock and unlock(self) or error "could not unlock
ADT - wrong master key?"
local tos = hiddenData[0] or error "corrupt stack"
tos = tos + 1
hiddenData[tos] = value
hiddenData[0] = tos
return value
end,
new = function (self)
self.newkey = {[0] = 0}
self.newkey = nil -- block subsequent use of new(key)
end,
pop = function (self, unlock)
local hiddenData = unlock and unlock(self) or error "could not
unlock ADT - wrong master key?"
local tos = hiddenData[0] or error "corrupt stack"
if tos <= 0 then
return nil
end
hiddenData[0] = tos - 1
return hiddenData[tos]
end
}
-- here we secure it
local t,unlockFun = create_secure_ADT("secret",stackADT)
local ADTAccessCapability = unlockFun("secret")
-- tests
-- create new stack
t:new()
-- push 3 values
t:push(1,ADTAccessCapability)
t:push(2,ADTAccessCapability)
t:push(3,ADTAccessCapability)
-- pop values and verify expected stack behaviour
assert(t:pop(ADTAccessCapability) == 3 and
t:pop(ADTAccessCapability) == 2 and t:pop(ADTAccessCapability) == 1)
-- cannot overwrite keys
assert(not pcall(function() t.new = nil end))
-- cannot use data type methods without knowing secret that unlocks them
assert(not pcall(function() t:push(4,unlockFun("wrong secret")) end))
-- cannot create stack again
assert(not pcall(function() t:new() end))
-- cannot use newkey once it's disabled
assert(not pcall(function() t.newkey = "foo" end))
print "Unit test passed"
end