diff --git a/components/modules/Kconfig b/components/modules/Kconfig index 3a51391d..d8c2ff7b 100644 --- a/components/modules/Kconfig +++ b/components/modules/Kconfig @@ -80,32 +80,6 @@ config LUA_MODULE_CRYPTO help Includes the crypto module. -menu "Crypto module hashing algorithms" - depends on LUA_MODULE_CRYPTO - - config CRYPTO_HASH_SHA1 - bool "SHA1" - default "y" - help - Includes the SHA1 hashing algorithm - config CRYPTO_HASH_SHA256 - bool "SHA256 and SHA224" - default "y" - help - Includes the SHA256 and SHA224 hashing algorithms - config CRYPTO_HASH_SHA512 - bool "SHA512 and SHA384" - default "y" - help - Includes the SHA256 and SHA384 hashing algorithms - config CRYPTO_HASH_MD5 - bool "MD5" - default "n" - help - Includes the MD5 hashing algorithm -endmenu - - config LUA_MODULE_DAC bool "DAC module" default "n" diff --git a/components/modules/crypto.c b/components/modules/crypto.c index ab3e04c8..f1e1fbf4 100644 --- a/components/modules/crypto.c +++ b/components/modules/crypto.c @@ -2,145 +2,51 @@ #include #include "lauxlib.h" #include "lmem.h" -#include "mbedtls/md5.h" -#include "mbedtls/sha1.h" -#include "mbedtls/sha256.h" -#include "mbedtls/sha512.h" +#include "mbedtls/md.h" #include "module.h" #include "platform.h" #define HASH_METATABLE "crypto.hasher" -// The following function typedefs aim to generalize mbedtls functions -// so that we can use the same code independent of what hashing algorithm -typedef void (*hash_init_t)(void* ctx); -typedef int (*hash_starts_ret_t)(void* ctx); -typedef int (*hash_update_ret_t)(void* ctx, const unsigned char* input, size_t ilen); -typedef int (*hash_finish_ret_t)(void* ctx, unsigned char* output); -typedef void (*hash_free_t)(void* ctx); - -// algo_info_t describes a hashing algorithm and the mbedtls functions -// for initializing, hashing data, finalizing and freeing resources. +// algo_info_t describes a hashing algorithm and output size typedef struct { const char* name; const size_t size; - const size_t context_size; - const hash_init_t init; - const hash_starts_ret_t starts; - const hash_update_ret_t update; - const hash_finish_ret_t finish; - const hash_free_t free; + const mbedtls_md_type_t type; } algo_info_t; // hash_context_t contains information about an ongoing hash operation typedef struct { - void* mbedtls_context; + mbedtls_md_context_t mbedtls_context; const algo_info_t* ainfo; + bool hmac_mode; } hash_context_t; -// if SHA256+SHA224 are enabled, the following two functions -// allow to call mbedtls appropriately depending on the algorithm -#ifdef CONFIG_CRYPTO_HASH_SHA256 -static int sha256_starts_ret(mbedtls_sha256_context* ctx) { - return mbedtls_sha256_starts_ret(ctx, false); // false=SHA256 -} -static int sha224_starts_ret(mbedtls_sha256_context* ctx) { - return mbedtls_sha256_starts_ret(ctx, true); // true=SHA224 -} -#endif - -// if SHA512+SHA384 are enabled, the following two functions -// allow to call mbedtls appropriately depending on the algorithm -#ifdef CONFIG_CRYPTO_HASH_SHA512 -static int sha512_starts_ret(mbedtls_sha512_context* ctx) { - return mbedtls_sha512_starts_ret(ctx, false); // false=SHA512 -} -static int sha384_starts_ret(mbedtls_sha512_context* ctx) { - return mbedtls_sha512_starts_ret(ctx, true); // true=SHA384 -} -#endif - // the constant algorithms array below contains a table of functions and other // information about each enabled hashing algorithm static const algo_info_t algorithms[] = { -#ifdef CONFIG_CRYPTO_HASH_SHA1 - { - "SHA1", - 20, - sizeof(mbedtls_sha1_context), - (hash_init_t)mbedtls_sha1_init, - (hash_starts_ret_t)mbedtls_sha1_starts_ret, - (hash_update_ret_t)mbedtls_sha1_update_ret, - (hash_finish_ret_t)mbedtls_sha1_finish_ret, - (hash_free_t)mbedtls_sha1_free, - }, -#endif -#ifdef CONFIG_CRYPTO_HASH_SHA256 - { - "SHA256", - 32, - sizeof(mbedtls_sha256_context), - (hash_init_t)mbedtls_sha256_init, - (hash_starts_ret_t)sha256_starts_ret, - (hash_update_ret_t)mbedtls_sha256_update_ret, - (hash_finish_ret_t)mbedtls_sha256_finish_ret, - (hash_free_t)mbedtls_sha256_free, - }, - { - "SHA224", - 32, - sizeof(mbedtls_sha256_context), - (hash_init_t)mbedtls_sha256_init, - (hash_starts_ret_t)sha224_starts_ret, - (hash_update_ret_t)mbedtls_sha256_update_ret, - (hash_finish_ret_t)mbedtls_sha256_finish_ret, - (hash_free_t)mbedtls_sha256_free, - }, -#endif -#ifdef CONFIG_CRYPTO_HASH_SHA512 - { - "SHA512", - 64, - sizeof(mbedtls_sha512_context), - (hash_init_t)mbedtls_sha512_init, - (hash_starts_ret_t)sha512_starts_ret, - (hash_update_ret_t)mbedtls_sha512_update_ret, - (hash_finish_ret_t)mbedtls_sha512_finish_ret, - (hash_free_t)mbedtls_sha512_free, - }, - { - "SHA384", - 64, - sizeof(mbedtls_sha512_context), - (hash_init_t)mbedtls_sha512_init, - (hash_starts_ret_t)sha384_starts_ret, - (hash_update_ret_t)mbedtls_sha512_update_ret, - (hash_finish_ret_t)mbedtls_sha512_finish_ret, - (hash_free_t)mbedtls_sha512_free, - }, -#endif -#ifdef CONFIG_CRYPTO_HASH_MD5 - { - "MD5", - 16, - sizeof(mbedtls_md5_context), - (hash_init_t)mbedtls_md5_init, - (hash_starts_ret_t)mbedtls_md5_starts_ret, - (hash_update_ret_t)mbedtls_md5_update_ret, - (hash_finish_ret_t)mbedtls_md5_finish_ret, - (hash_free_t)mbedtls_md5_free, - }, -#endif + { "MD5", 16, MBEDTLS_MD_MD5 }, + { "RIPEMD160", 20, MBEDTLS_MD_RIPEMD160 }, + { "SHA1", 20, MBEDTLS_MD_SHA1 }, + { "SHA224", 32, MBEDTLS_MD_SHA224 }, + { "SHA256", 32, MBEDTLS_MD_SHA256 }, + { "SHA384", 64, MBEDTLS_MD_SHA384 }, + { "SHA512", 64, MBEDTLS_MD_SHA512 }, }; + //NUM_ALGORITHMS contains the actual number of enabled algorithms const int NUM_ALGORITHMS = sizeof(algorithms) / sizeof(algo_info_t); // crypto_new_hash (LUA: hasher = crypto.new_hash(algo)) allocates // a hashing context for the requested algorithm -static int crypto_new_hash(lua_State* L) { - const char* algo = luaL_checkstring(L, 1); - const algo_info_t* ainfo = NULL; +static int crypto_new_hash_or_hmac(lua_State* L, bool is_hmac) { + const algo_info_t *ainfo = NULL; + const char *algo = luaL_checkstring(L, 1); + const unsigned char *key = NULL; + size_t key_len = 0; + if (is_hmac) + key = (const unsigned char *)luaL_checklstring(L, 2, &key_len); for (int i = 0; i < NUM_ALGORITHMS; i++) { if (strcasecmp(algo, algorithms[i].name) == 0) { @@ -150,7 +56,7 @@ static int crypto_new_hash(lua_State* L) { } if (ainfo == NULL) { - luaL_error(L, "Unsupported algorithm: %s", algo); // returns + return luaL_error(L, "Unsupported algorithm: %s", algo); } // Instantiate a hasher object as a Lua userdata object @@ -161,19 +67,35 @@ static int crypto_new_hash(lua_State* L) { luaL_getmetatable(L, HASH_METATABLE); lua_setmetatable(L, -2); - phctx->ainfo = ainfo; // save a pointer to the algorithm function table and information - phctx->mbedtls_context = luaM_malloc(L, ainfo->context_size); // make some space for the mbedtls context - if (phctx->mbedtls_context == NULL) { - luaL_error(L, "Out of memory allocating context"); - } + phctx->ainfo = ainfo; + phctx->hmac_mode = is_hmac; + + mbedtls_md_init(&phctx->mbedtls_context); + int err = + mbedtls_md_setup( + &phctx->mbedtls_context, + mbedtls_md_info_from_type(phctx->ainfo->type), + is_hmac); + if (phctx->hmac_mode) + err |= mbedtls_md_hmac_starts(&phctx->mbedtls_context, key, key_len); + else + err |= mbedtls_md_starts(&phctx->mbedtls_context); + if (err != 0) + return luaL_error(L, "Error starting context"); - ainfo->init(phctx->mbedtls_context); // initialize the hashing function - if (ainfo->starts(phctx->mbedtls_context) != 0) { - luaL_error(L, "Error starting context"); - } return 1; // one object returned, the hasher userdata object. } +static int crypto_new_hash(lua_State* L) { + return crypto_new_hash_or_hmac(L, false); +} + +static int crypto_new_hmac(lua_State* L) +{ + return crypto_new_hash_or_hmac(L, true); +} + + // crypto_hash_update (LUA: hasher:update(data)) submits data // to be hashed. static int crypto_hash_update(lua_State* L) { @@ -184,10 +106,15 @@ static int crypto_hash_update(lua_State* L) { // retrieve the input string: const unsigned char* input = (const unsigned char*)luaL_checklstring(L, 2, &size); + int err = 0; // call the update hashing function: - if (phctx->ainfo->update(phctx->mbedtls_context, input, size) != 0) { + if (phctx->hmac_mode) + err = mbedtls_md_hmac_update(&phctx->mbedtls_context, input, size); + else + err = mbedtls_md_update(&phctx->mbedtls_context, input, size); + + if (err != 0) luaL_error(L, "Error updating hash"); - } return 0; // no return value } @@ -201,10 +128,14 @@ static int crypto_hash_finalize(lua_State* L) { // reserve some space to retrieve the output hash, according to the current algorithm unsigned char output[phctx->ainfo->size]; + int err = 0; // call the hash finish function to retrieve the result - if (phctx->ainfo->finish(phctx->mbedtls_context, output) != 0) { + if (phctx->hmac_mode) + err = mbedtls_md_hmac_finish(&phctx->mbedtls_context, output); + else + err = mbedtls_md_finish(&phctx->mbedtls_context, output); + if (err != 0) luaL_error(L, "Error finalizing hash"); - } // pack the output into a lua string lua_pushlstring(L, (const char*)output, phctx->ainfo->size); @@ -217,16 +148,7 @@ static int crypto_hash_finalize(lua_State* L) { static int crypto_hash_gc(lua_State* L) { // retrieve the hashing context: hash_context_t* phctx = (hash_context_t*)luaL_checkudata(L, 1, HASH_METATABLE); - - // if the mbedtls context is NULL, it means allocation failed in new_hash(), so nothing to do. - if (phctx->mbedtls_context == NULL) - return 0; - - // free mbedtls-related resources for this hash operation: - phctx->ainfo->free(phctx->mbedtls_context); - - // free the memory allocated to store the mbedtls context: - luaM_freemem(L, phctx->mbedtls_context, phctx->ainfo->context_size); + mbedtls_md_free(&phctx->mbedtls_context); return 0; } @@ -241,6 +163,7 @@ static const LUA_REG_TYPE crypto_hasher_map[] = { // This table defines the functions of the crypto module: static const LUA_REG_TYPE crypto_map[] = { {LSTRKEY("new_hash"), LFUNCVAL(crypto_new_hash)}, + {LSTRKEY("new_hmac"), LFUNCVAL(crypto_new_hmac)}, {LNILKEY, LNILVAL}}; // luaopen_crypto is the crypto module initialization function diff --git a/docs/modules/crypto.md b/docs/modules/crypto.md index ea81d51d..b9a87d59 100644 --- a/docs/modules/crypto.md +++ b/docs/modules/crypto.md @@ -1,30 +1,32 @@ # crypto Module | Since | Origin / Contributor | Maintainer | Source | | :----- | :-------------------- | :---------- | :------ | -| 2019-01-13 | [Javier Peletier](https://github.com/jpeletier) | [Javier Peletier](https://github.com/jpeletier) | [crypto.c](../../../components/modules/crypto.c)| +| 2019-01-13 | [Javier Peletier](https://github.com/jpeletier), [Johny Mattsson](https://github.com/jmattsson) | [Javier Peletier](https://github.com/jpeletier) | [crypto.c](../../../components/modules/crypto.c)| The crypto module provides various functions for working with cryptographic algorithms. -This is work in progress, for now only a number of hashing functions are available: - -* SHA1 -* SHA256 -* SHA224 -* SHA512 -* SHA384 +The following algorithms are supported, both in digest mode and in HMAC mode: * MD5 - -All except MD5 are enabled by default. To disable algorithms you don't need, find the "Crypto module hashing algorithms" under the NodeMCU modules section in menuconfig. +* SHA1 +* RIPEMD160 +* SHA224 +* SHA256 +* SHA384 +* SHA512 ## crypto.new_hash() -Create a digest/hash object that can have any number of strings added to it. Object has `update` and `finalize` functions. +Create a digest/hash object that can have any number of strings added to it. + +The returned object has `update(str)` and `finalize()` functions, for +streaming data through the hash function, and for finalizing and returning +the resulting digest. #### Syntax `hashobj = crypto.new_hash(algo)` #### Parameters -`algo` the hash algorithm to use, case insensitive string +- `algo` the hash algorithm to use, case insensitive string #### Returns Hasher object with `update` and `finalize` functions available. @@ -32,8 +34,38 @@ Hasher object with `update` and `finalize` functions available. #### Example ```lua hashobj = crypto.new_hash("SHA1") -hashobj:update("FirstString")) -hashobj:update("SecondString")) +hashobj:update("FirstString") +hashobj:update("SecondString") digest = hashobj:finalize() print(encoder.toHex(digest)) -``` \ No newline at end of file +``` + +## crypto.new_hmac() + +Create an object for calculating a HMAC (Hashed Message Authentication Code, +aka "signature"). A HMAC can be used to simultaneously verify both integrity +and authenticity of data. + +The returned object has `update(str)` and `finalize()` functions, for +streaming data through the hash function, and for finalizing and returning +the resulting signature. + + +#### Syntax +`hashobj = crypto.new_hmac(algo, key)` + +#### Parameters +- `algo` the hash algorithm to use, case insensitive string +- `key` the signing key (may be a binary string) + +#### Returns +Hasher object with `update` and `finalize` functions available. + +#### Example +```lua +hmac = crypto.new_hmac("SHA1", "top s3cr3t key") +hmac:update("Some data") +hmac:update("... more data") +signature = hmac:finalize() +print(encoder.toHex(signature)) +```