lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


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