Improved crypto module for ESP32, now with HMAC (#2815)
* Leaner, meaner crypto module; now with HMAC Based on my testing, mbedtls pulls in all its algorithm regardless of whether the NodeMCU crypto module was using them or not. As such, the space savings from omitting algorithms were only in the tens of bytes. By switching to using the mbedtls generic message digest interface, the crypto module itself could be shrunk in size and complexity. Despite adding support for HMAC on all algorithms (plus including RIPEMD160), this version is 330 bytes smaller. * Updated crypto module docs. * Removed superfluous brackets in crypto docs. Copy-paste considered harmful... >.>
This commit is contained in:
parent
1f1eeaf358
commit
e11087bfdf
|
@ -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"
|
||||
|
|
|
@ -2,145 +2,51 @@
|
|||
#include <string.h>
|
||||
#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
|
||||
|
|
|
@ -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))
|
||||
```
|
||||
```
|
||||
|
||||
## 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))
|
||||
```
|
||||
|
|
Loading…
Reference in New Issue