Added crypto.new_hmac() feature. (#1499)

Docs for crypto module refactored for easier maintenance.
This commit is contained in:
Johny Mattsson 2016-09-17 01:46:39 +10:00 committed by Marcel Stör
parent 19f80abb32
commit a112296850
4 changed files with 147 additions and 69 deletions

View File

@ -156,31 +156,22 @@ int ICACHE_FLASH_ATTR crypto_fhash (const digest_mech_info_t *mi,
}
int ICACHE_FLASH_ATTR crypto_hmac (const digest_mech_info_t *mi,
const char *data, size_t data_len,
const char *key, size_t key_len,
uint8_t *digest)
void crypto_hmac_begin (void *ctx, const digest_mech_info_t *mi,
const char *key, size_t key_len, uint8_t *k_opad)
{
if (!mi)
return EINVAL;
void *ctx = (void *)os_malloc (mi->ctx_size);
if (!ctx)
return ENOMEM;
// If key too long, it needs to be hashed before use
char tmp[mi->digest_size];
if (key_len > mi->block_size)
{
mi->create (ctx);
mi->update (ctx, key, key_len);
mi->finalize (digest, ctx);
key = digest;
mi->finalize (tmp, ctx);
key = tmp;
key_len = mi->digest_size;
mi->create (ctx); // refresh
}
const size_t bs = mi->block_size;
uint8_t k_ipad[bs];
uint8_t k_opad[bs];
os_memset (k_ipad, 0x36, bs);
os_memset (k_opad, 0x5c, bs);
@ -191,16 +182,42 @@ int ICACHE_FLASH_ATTR crypto_hmac (const digest_mech_info_t *mi,
k_opad[i] ^= key[i];
}
mi->create (ctx);
mi->update (ctx, k_ipad, bs);
mi->update (ctx, data, data_len);
}
void crypto_hmac_finalize (void *ctx, const digest_mech_info_t *mi,
const uint8_t *k_opad, uint8_t *digest)
{
mi->finalize (digest, ctx);
mi->create (ctx);
mi->update (ctx, k_opad, bs);
mi->update (ctx, k_opad, mi->block_size);
mi->update (ctx, digest, mi->digest_size);
mi->finalize (digest, ctx);
}
os_free (ctx);
int crypto_hmac (const digest_mech_info_t *mi,
const char *data, size_t data_len,
const char *key, size_t key_len,
uint8_t *digest)
{
if (!mi)
return EINVAL;
struct {
uint8_t ctx[mi->ctx_size];
uint8_t k_opad[mi->block_size];
} *tmp = os_malloc (sizeof (*tmp));
if (!tmp)
return ENOMEM;
mi->create (tmp->ctx);
crypto_hmac_begin (tmp->ctx, mi, key, key_len, tmp->k_opad);
mi->update (tmp->ctx, data, data_len);
crypto_hmac_finalize (tmp->ctx, mi, tmp->k_opad, digest);
os_free (tmp);
return 0;
}

View File

@ -66,7 +66,35 @@ int crypto_hash (const digest_mech_info_t *mi, const char *data, size_t data_len
int crypto_fhash (const digest_mech_info_t *mi, read_fn read, int readarg, uint8_t *digest);
/**
* Generate a HMAC signature.
* Commence calculating a HMAC signature.
*
* Once this fuction returns successfully, the @c ctx may be updated with
* data in multiple passes uses @c mi->update(), before being finalized via
* @c crypto_hmac_finalize().
*
* @param ctx A created context for the given mech @c mi.
* @param mi A mech from @c crypto_digest_mech().
* @param key The key bytes to use.
* @param key_len Number of bytes the @c key comprises.
* @param k_opad Buffer of size @c mi->block_size bytes to store the second
* round of key material in.
* Must be passed to @c crypto_hmac_finalize().
*/
void crypto_hmac_begin (void *ctx, const digest_mech_info_t *mi, const char *key, size_t key_len, uint8_t *k_opad);
/**
* Finalizes calculating a HMAC signature.
* @param ctx The created context used with @c crypto_hmac_begin().
* @param mi The mech used with @c crypto_hmac_begin().
* @param k_opad Second round of keying material, obtained
* from @c crypto_hmac_begin().
* @param digest Output buffer, must be at least @c mi->digest_size in size.
*/
void crypto_hmac_finalize (void *ctx, const digest_mech_info_t *mi, const uint8_t *k_opad, uint8_t *digest);
/**
* Generate a HMAC signature in one pass.
* Implemented in terms of @c crypto_hmac_begin() / @c crypto_hmac_end().
* @param mi A mech from @c crypto_digest_mech(). A null pointer @c mi
* is harmless, but will of course result in an error return.
* @param data The data to generate a signature for.

View File

@ -9,11 +9,18 @@
#include "vfs.h"
#include "../crypto/digests.h"
#include "../crypto/mech.h"
#include "lmem.h"
#include "user_interface.h"
#include "rom.h"
typedef struct {
const digest_mech_info_t *mech_info;
void *ctx;
uint8_t *k_opad;
} digest_user_datum_t;
/**
* hash = crypto.sha1(input)
*
@ -150,11 +157,6 @@ static int crypto_lhash (lua_State *L)
return 1;
}
typedef struct digest_user_datum_t {
const digest_mech_info_t *mech_info;
void *ctx;
} digest_user_datum_t;
/* General Usage for extensible hash functions:
* sha = crypto.new_hash("MD5")
* sha.update("Data")
@ -162,19 +164,30 @@ typedef struct digest_user_datum_t {
* strdigest = crypto.toHex(sha.finalize())
*/
/* crypto.new_hash("MECHTYPE") */
static int crypto_new_hash (lua_State *L)
#define WANT_HASH 0
#define WANT_HMAC 1
static int crypto_new_hash_hmac (lua_State *L, int what)
{
const digest_mech_info_t *mi = crypto_digest_mech (luaL_checkstring (L, 1));
if (!mi)
return bad_mech (L);
void *ctx = os_malloc (mi->ctx_size);
if (ctx==NULL)
return bad_mem (L);
size_t len = 0;
const char *key = 0;
uint8_t *k_opad = 0;
if (what == WANT_HMAC)
{
key = luaL_checklstring (L, 2, &len);
k_opad = luaM_malloc (L, mi->block_size);
}
void *ctx = luaM_malloc (L, mi->ctx_size);
mi->create (ctx);
if (what == WANT_HMAC)
crypto_hmac_begin (ctx, mi, key, len, k_opad);
// create a userdataum with specific metatable
digest_user_datum_t *dudat = (digest_user_datum_t *)lua_newuserdata(L, sizeof(digest_user_datum_t));
luaL_getmetatable(L, "crypto.hash");
@ -183,10 +196,24 @@ static int crypto_new_hash (lua_State *L)
// Set pointers to the mechanics and CTX
dudat->mech_info = mi;
dudat->ctx = ctx;
dudat->k_opad = k_opad;
return 1; // Pass userdata object back
}
/* crypto.new_hash("MECHTYPE") */
static int crypto_new_hash (lua_State *L)
{
return crypto_new_hash_hmac (L, WANT_HASH);
}
/* crypto.new_hmac("MECHTYPE", "KEY") */
static int crypto_new_hmac (lua_State *L)
{
return crypto_new_hash_hmac (L, WANT_HMAC);
}
/* Called as object, params:
1 - userdata "this"
2 - new string to add to the hash state */
@ -197,7 +224,6 @@ static int crypto_hash_update (lua_State *L)
size_t sl;
dudat = (digest_user_datum_t *)luaL_checkudata(L, 1, "crypto.hash");
luaL_argcheck(L, dudat, 1, "crypto.hash expected");
const digest_mech_info_t *mi = dudat->mech_info;
@ -217,12 +243,14 @@ static int crypto_hash_finalize (lua_State *L)
size_t sl;
dudat = (digest_user_datum_t *)luaL_checkudata(L, 1, "crypto.hash");
luaL_argcheck(L, dudat, 1, "crypto.hash expected");
const digest_mech_info_t *mi = dudat->mech_info;
uint8_t digest[mi->digest_size]; // Allocate as local
mi->finalize (digest, dudat->ctx);
if (dudat->k_opad)
crypto_hmac_finalize (dudat->ctx, mi, dudat->k_opad, digest);
else
mi->finalize (digest, dudat->ctx);
lua_pushlstring (L, digest, sizeof (digest));
return 1;
@ -235,9 +263,11 @@ static int crypto_hash_gcdelete (lua_State *L)
digest_user_datum_t *dudat;
dudat = (digest_user_datum_t *)luaL_checkudata(L, 1, "crypto.hash");
luaL_argcheck(L, dudat, 1, "crypto.hash expected");
os_free(dudat->ctx);
// luaM_free() uses type info to obtain original size, so have to delve
// one level deeper and explicitly pass the size due to void*
luaM_realloc_ (L, dudat->ctx, dudat->mech_info->ctx_size, 0);
luaM_free (L, dudat->k_opad);
return 0;
}
@ -386,6 +416,7 @@ static const LUA_REG_TYPE crypto_map[] = {
{ LSTRKEY( "fhash" ), LFUNCVAL( crypto_flhash ) },
{ LSTRKEY( "new_hash" ), LFUNCVAL( crypto_new_hash ) },
{ LSTRKEY( "hmac" ), LFUNCVAL( crypto_lhmac ) },
{ LSTRKEY( "new_hmac" ), LFUNCVAL( crypto_new_hmac ) },
{ LSTRKEY( "encrypt" ), LFUNCVAL( lcrypto_encrypt ) },
{ LSTRKEY( "decrypt" ), LFUNCVAL( lcrypto_decrypt ) },
{ LNILKEY, LNILVAL }

View File

@ -5,6 +5,16 @@
The crypto modules provides various functions for working with cryptographic algorithms.
The following encryption/decryption algorithms/modes are supported:
- `"AES-ECB"` for 128-bit AES in ECB mode (NOT recommended)
- `"AES-CBC"` for 128-bit AES in CBC mode
The following hash algorithms are supported:
- MD2 (not available by default, has to be explicitly enabled in `app/include/user_config.h`)
- MD5
- SHA1
- SHA256, SHA384, SHA512 (unless disabled in `app/include/user_config.h`)
## crypto.encrypt()
Encrypts Lua strings.
@ -13,9 +23,7 @@ Encrypts Lua strings.
`crypto.encrypt(algo, key, plain [, iv])`
#### Parameters
- `algo` the name of the encryption algorithm to use, one of
- `"AES-ECB"` for 128-bit AES in ECB mode
- `"AES-CBC"` for 128-bit AES in CBC mode
- `algo` the name of a supported encryption algorithm to use
- `key` the encryption key as a string; for AES encryption this *MUST* be 16 bytes long
- `plain` the string to encrypt; it will be automatically zero-padded to a 16-byte boundary if necessary
- `iv` the initilization vector, if using AES-CBC; defaults to all-zero if not given
@ -40,9 +48,7 @@ Decrypts previously encrypted data.
`crypto.decrypt(algo, key, cipher [, iv])`
#### Parameters
- `algo` the name of the encryption algorithm to use, one of
- `"AES-ECB"` for 128-bit AES in ECB mode
- `"AES-CBC"` for 128-bit AES in CBC mode
- `algo` the name of a supported encryption algorithm to use
- `key` the encryption key as a string; for AES encryption this *MUST* be 16 bytes long
- `cipher` the cipher text to decrypt (as obtained from `crypto.encrypt()`)
- `iv` the initilization vector, if using AES-CBC; defaults to all-zero if not given
@ -75,13 +81,6 @@ Compute a cryptographic hash of a a file.
- `algo` the hash algorithm to use, case insensitive string
- `filename` the path to the file to hash
Supported hash algorithms are:
- MD2 (not available by default, has to be explicitly enabled in `app/include/user_config.h`)
- MD5
- SHA1
- SHA256, SHA384, SHA512 (unless disabled in `app/include/user_config.h`)
#### Returns
A binary string containing the message digest. To obtain the textual version (ASCII hex characters), please use [`crypto.toHex()`](#cryptotohex ).
@ -101,13 +100,6 @@ Compute a cryptographic hash of a Lua string.
`algo` the hash algorithm to use, case insensitive string
`str` string to hash contents of
Supported hash algorithms are:
- MD2 (not available by default, has to be explicitly enabled in `app/include/user_config.h`)
- MD5
- SHA1
- SHA256, SHA384, SHA512 (unless disabled in `app/include/user_config.h`)
#### Returns
A binary string containing the message digest. To obtain the textual version (ASCII hex characters), please use [`crypto.toHex()`](#cryptotohex ).
@ -126,13 +118,6 @@ Create a digest/hash object that can have any number of strings added to it. Obj
#### Parameters
`algo` the hash algorithm to use, case insensitive string
Supported hash algorithms are:
- MD2 (not available by default, has to be explicitly enabled in `app/include/user_config.h`)
- MD5
- SHA1
- SHA256, SHA384, SHA512 (unless disabled in `app/include/user_config.h`)
#### Returns
Userdata object with `update` and `finalize` functions available.
@ -157,13 +142,6 @@ Compute a [HMAC](https://en.wikipedia.org/wiki/Hash-based_message_authentication
- `str` data to calculate the hash for
- `key` key to use for signing, may be a binary string
Supported hash algorithms are:
- MD2 (not available by default, has to be explicitly enabled in `app/include/user_config.h`)
- MD5
- SHA1
- SHA256, SHA384, SHA512 (unless disabled in `app/include/user_config.h`)
#### Returns
A binary string containing the HMAC signature. Use [`crypto.toHex()`](#cryptotohex ) to obtain the textual version.
@ -172,6 +150,30 @@ A binary string containing the HMAC signature. Use [`crypto.toHex()`](#cryptotoh
print(crypto.toHex(crypto.hmac("sha1","abc","mysecret")))
```
## crypto.new_hmac()
Create a hmac object that can have any number of strings added to it. Object has `update` and `finalize` functions.
#### Syntax
`hmacobj = crypto.new_hmac(algo, key)`
#### Parameters
- `algo` the hash algorithm to use, case insensitive string
- `key` the key to use (may be a binary string)
#### Returns
Userdata object with `update` and `finalize` functions available.
#### Example
```lua
hmacobj = crypto.new_hmac("SHA1", "s3kr3t")
hmacobj:update("FirstString"))
hmacobj:update("SecondString"))
digest = hmacobj:finalize()
print(crypto.toHex(digest))
```
## crypto.mask()
Applies an XOR mask to a Lua string. Note that this is not a proper cryptographic mechanism, but some protocols may use it nevertheless.