10

The code related to this question is here: https://github.com/jchester/lua-polarssl/tree/master/src

Currently I'm trying to wrap one part of the PolarSSL library (http://polarssl.org) to give me access to SHA-512 HMACs (luacrypto does not provide this).

The API I'm aiming for is of the form:

a_sha512_hash = polarssl.hash.sha512('text')

or more fully

local polarssl = require 'polarssl'
local hash = polarssl.hash

a_sha512_hash = hash.sha512('test')

If you refer to polarssl.c in the link above, you'll see I've written functions that wrap PolarSSL code. Then I'm trying to build the function tables thus:

LUA_API int luaopen_polarssl( lua_State *L ) {
  static const struct luaL_Reg core[] = {
    { NULL, NULL }
  };

  static const struct luaL_Reg hash_functions[] = {
    { "sha512", hash_sha512 },
    { "sha384", hash_sha384 },
    { NULL, NULL }
  };

  static const struct luaL_Reg hmac_functions[] = {
    { "sha512", hmac_sha512 },
    { "sha384", hmac_sha384 },
    { NULL, NULL }
  };

  luaL_register( L, CORE_MOD_NAME, core );
  luaL_register( L, HASH_MOD_NAME, hash_functions );
  luaL_register( L, HMAC_MOD_NAME, hmac_functions );

  return 1;
}

Where CORE_MOD_NAME = 'polarssl', HASH_MOD_NAME = 'polarssl.hash', HMAC_MOD_NAME = 'polarssl.hmac'.

When I run a test script similar to the Lua code at the top of this question, I get this:

lua: test.lua:23: attempt to index global 'polarssl' (a nil value)
stack traceback:
    test.lua:23: in main chunk
    [C]: ?

I've tried looking for examples of how to achieve this module.submodule approach (eg naim vs luasockets), but everyone seems to have a different way of achieving it. I'm completely lost.

Daniel Roethlisberger
  • 6,958
  • 2
  • 41
  • 59
Jacques Chester
  • 628
  • 1
  • 4
  • 13

2 Answers2

15

everyone seems to have a different way of achieving it.

That's Lua; everyone does it their own way. It's Lua's greatest strength and greatest weakness: the language provides mechanisms, not policies.

The first thing you need to do is stop using luaL_register. Yes, I know it's convenient. But you want something special, and luaL_register isn't going to help you get it.

What you want is to create a table that contains a table that contains one or more functions. So... do that.

Create a table.

lua_newtable(L);

That was easy. The function pushes a table on to the stack, so our stack now has a table on top of it. This is the table we will return.

Now, we need to create a new table to go inside the old one.

lua_newtable(L);

Again, easy. Next, we want to put the function we want to go into that table on the stack.

lua_pushcfunction(L, hash_sha512);

So the stack has three things: the destination table, the "hash" table (we'll get to "naming" it in a second), and the function we want to put into the "hash" table.

So put the function into the hash table.

lua_setfield(L, -2, "sha512");

This takes whatever is on top of the stack and sets it into the field named "sha512" on the table at the -2 index on the stack. That's where our "hash" table is. After this function completes, it removes the top item from the stack. This leaves the "hash" table at the top.

We can repeat the process for the second function:

lua_pushcfunction(L, hash_sha384);
lua_setfield(L, -2, "sha384");

Now, we want to put the "hash" table into the table we want to return. That's done easily enough with another lua_setfield call:

lua_setfield(L, -2, "hash");

Remember: this function takes whatever is on top of the stack. At this point, the table we want to return (which will be our module's table) is on the stack.

We can repeat this process for the "hmac" table:

lua_newtable(L); //Create "hmac" table
lua_pushcfunction(L, hmac_sha512);
lua_setfield(L, -2, "sha512");
lua_pushcfunction(L, hmac_sha384);  
lua_setfield(L, -2, "sha384");
lua_setfield(L, -2, "hmac"); //Put the "hmac" table into our module table

The module's table now has two entries in it: "hash" and "hmac". Both are tables with two functions in them.

We can stick it into the global table with this:

lua_pushvalue(L, -1);
lua_setfield(L, LUA_GLOBALSINDEX, "polarssl");

Not every module maker wants to do that. Some prefer to force people to use the local polarssl = require "polarssl" syntax, to avoid polluting the global namespace. It's up to you.

But what you must do either way is to return this table. Return 1 from your luaopen function, to let Lua know that there is one return value. The lua_pushvalue call above exists for the sole purpose of copying the table (remember: tables are referenced, so it's like copying a pointer). That way, when you use lua_setfield, the copy gets removed from the stack, while the original remains to be used as a return value.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Thankyou. I was a bit puzzled at first, but sketching the stack movements on paper helped me catch on to the process. – Jacques Chester Mar 02 '12 at 04:55
  • 3
    +1 for going to some trouble to discuss the return value from `require` ... a lot of people don't really seem to be aware that recommended practice has shifted from "just stick your module in a global" to "return your module from require, and the caller should put that in a local variable" – snogglethorpe Mar 02 '12 at 22:51
2

Not directly related to the question, but the following statement is not entirely true:

Currently I'm trying to wrap one part of the PolarSSL library (http://polarssl.org) to give me access to SHA-512 HMACs (luacrypto does not provide this).

I do not know which version of LuaCrypto are you referring to, but this LuaCrypto fork does provide SHA-512 HMACs, and also any other digest type supported by OpenSSL automatically. Just pass "sha512" as the digest type:

hmac.digest("sha512", message, key)

The documentation states only a part of the supported digest types, the complete list can be retrieved by calling crypto.list("digests").

table.foreach(crypto.list("digests"), print)

When I think about it, even original LuaCrypto should support SHA-512.

Michal Kottman
  • 16,375
  • 3
  • 47
  • 62
  • Thanks, I wasn't aware of it. However, I was running off the "main" fork which doesn't wrap SHA512. I chose to wrap PolarSSL because it's small and the API is very simple -- every module is standalone and can be independently compiled. For example, I've compiled only the SHA512/384 module. Total size: 22kb. – Jacques Chester Mar 03 '12 at 01:36
  • That is indeed a very reasonable size! Looks great if you want to embed Lua with cryptographic capabilities. – Michal Kottman Mar 03 '12 at 09:43