Johny Mattsson 526d21dab4 Major cleanup - c_whatever is finally history. (#2838)
The PR removed the bulk of non-newlib headers from the NodeMCU source base.  
app/libc has now been cut down to the bare minimum overrides to shadow the 
corresponding functions in the SDK's libc. The old c_xyz.h headerfiles have been 
nuked in favour of the standard <xyz.h> headers, with a few exceptions over in 
sdk-overrides. Again, shipping a libc.a without headers is a terrible thing to do. We're 
still living on a prayer that libc was configured the same was as a default-configured
xtensa gcc toolchain assumes it is. That part I cannot do anything about, unfortunately, 
but it's no worse than it has been before.

This enables our source files to compile successfully using the standard header files, 
and use the typical malloc()/calloc()/realloc()/free(), the strwhatever()s and 
memwhatever()s. These end up, through macro and linker magic, mapped to the 
appropriate SDK or ROM functions.
2019-07-22 00:58:21 +03:00

417 lines
11 KiB
C

// Module for cryptography
#include <errno.h>
#include "module.h"
#include "lauxlib.h"
#include "platform.h"
#include "c_types.h"
#include <stdlib.h>
#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)
*
* Calculates raw SHA1 hash of input string.
* Input is arbitrary string, output is raw 20-byte hash as string.
*/
static int crypto_sha1( lua_State* L )
{
SHA1_CTX ctx;
uint8_t digest[20];
// Read the string from lua (with length)
int len;
const char* msg = luaL_checklstring(L, 1, &len);
// Use the SHA* functions in the rom
SHA1Init(&ctx);
SHA1Update(&ctx, msg, len);
SHA1Final(digest, &ctx);
// Push the result as a lua string
lua_pushlstring(L, digest, 20);
return 1;
}
#ifdef LUA_USE_MODULES_ENCODER
static int call_encoder( lua_State* L, const char *function ) {
if (lua_gettop(L) != 1) {
luaL_error(L, "%s must have one argument", function);
}
lua_getfield(L, LUA_GLOBALSINDEX, "encoder");
if (!lua_istable(L, -1) && !lua_isrotable(L, -1)) { // also need table just in case encoder has been overloaded
luaL_error(L, "Cannot find encoder.%s", function);
}
lua_getfield(L, -1, function);
lua_insert(L, 1); //move function below the argument
lua_pop(L, 1); //and dump the encoder rotable from stack.
lua_call(L,1,1); // call encoder.xxx(string)
return 1;
}
static int crypto_base64_encode (lua_State* L) {
return call_encoder(L, "toBase64");
}
static int crypto_hex_encode (lua_State* L) {
return call_encoder(L, "toHex");
}
#else
static const char* bytes64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* encoded = crypto.toBase64(raw)
*
* Encodes raw binary string as base64 string.
*/
static int crypto_base64_encode( lua_State* L )
{
int len, i;
const char* msg = luaL_checklstring(L, 1, &len);
luaL_Buffer out;
luaL_buffinit(L, &out);
for (i = 0; i < len; i += 3) {
int a = msg[i];
int b = (i + 1 < len) ? msg[i + 1] : 0;
int c = (i + 2 < len) ? msg[i + 2] : 0;
luaL_addchar(&out, bytes64[a >> 2]);
luaL_addchar(&out, bytes64[((a & 3) << 4) | (b >> 4)]);
luaL_addchar(&out, (i + 1 < len) ? bytes64[((b & 15) << 2) | (c >> 6)] : 61);
luaL_addchar(&out, (i + 2 < len) ? bytes64[(c & 63)] : 61);
}
luaL_pushresult(&out);
return 1;
}
/**
* encoded = crypto.toHex(raw)
*
* Encodes raw binary string as hex string.
*/
static int crypto_hex_encode( lua_State* L)
{
int len, i;
const char* msg = luaL_checklstring(L, 1, &len);
luaL_Buffer out;
luaL_buffinit(L, &out);
for (i = 0; i < len; i++) {
luaL_addchar(&out, crypto_hexbytes[msg[i] >> 4]);
luaL_addchar(&out, crypto_hexbytes[msg[i] & 0xf]);
}
luaL_pushresult(&out);
return 1;
}
#endif
/**
* masked = crypto.mask(message, mask)
*
* Apply a mask (repeated if shorter than message) as XOR to each byte.
*/
static int crypto_mask( lua_State* L )
{
int len, mask_len, i;
const char* msg = luaL_checklstring(L, 1, &len);
const char* mask = luaL_checklstring(L, 2, &mask_len);
luaL_Buffer b;
if(mask_len <= 0)
return luaL_error(L, "invalid argument: mask");
luaL_buffinit(L, &b);
for (i = 0; i < len; i++) {
luaL_addchar(&b, msg[i] ^ mask[i % mask_len]);
}
luaL_pushresult(&b);
return 1;
}
static inline int bad_mech (lua_State *L) { return luaL_error (L, "unknown hash mech"); }
static inline int bad_mem (lua_State *L) { return luaL_error (L, "insufficient memory"); }
static inline int bad_file (lua_State *L) { return luaL_error (L, "file does not exist"); }
/* rawdigest = crypto.hash("MD5", str)
* strdigest = crypto.toHex(rawdigest)
*/
static int crypto_lhash (lua_State *L)
{
const digest_mech_info_t *mi = crypto_digest_mech (luaL_checkstring (L, 1));
if (!mi)
return bad_mech (L);
size_t len = 0;
const char *data = luaL_checklstring (L, 2, &len);
uint8_t digest[mi->digest_size];
if (crypto_hash (mi, data, len, digest) != 0)
return bad_mem (L);
lua_pushlstring (L, digest, sizeof (digest));
return 1;
}
/* General Usage for extensible hash functions:
* sha = crypto.new_hash("MD5")
* sha.update("Data")
* sha.update("Data2")
* strdigest = crypto.toHex(sha.finalize())
*/
#define WANT_HASH 0
#define WANT_HMAC 1
static int crypto_new_hash_hmac (lua_State *L, int what)
{
// get pointer to relevant hash_mechs table entry in app/crypto/digest.c. Note that
// the size of the table needed is dependent on the the digest type
const digest_mech_info_t *mi = crypto_digest_mech (luaL_checkstring (L, 1));
if (!mi)
return bad_mech (L);
size_t len = 0, k_opad_len = 0, udlen;
const char *key = NULL;
uint8_t *k_opad = NULL;
if (what == WANT_HMAC)
{ // The key and k_opad are only used for HMAC; these default to NULLs for HASH
key = luaL_checklstring (L, 2, &len);
k_opad_len = mi->block_size;
}
// create a userdatum with specific metatable. This comprises the ud header,
// the encrypto context block, and an optional HMAC block as a single allocation
// unit
udlen = sizeof(digest_user_datum_t) + mi->ctx_size + k_opad_len;
digest_user_datum_t *dudat = (digest_user_datum_t *)lua_newuserdata(L, udlen);
luaL_getmetatable(L, "crypto.hash"); // and set its metatable to the crypto.hash table
lua_setmetatable(L, -2);
void *ctx = dudat + 1; // The context block immediately follows the digest_user_datum
mi->create (ctx);
if (what == WANT_HMAC) {
// The k_opad block immediately follows the context block
k_opad = (char *)ctx + mi->ctx_size;
crypto_hmac_begin (ctx, mi, key, len, k_opad);
}
// 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 */
static int crypto_hash_update (lua_State *L)
{
NODE_DBG("enter crypto_hash_update.\n");
digest_user_datum_t *dudat;
size_t sl;
dudat = (digest_user_datum_t *)luaL_checkudata(L, 1, "crypto.hash");
const digest_mech_info_t *mi = dudat->mech_info;
size_t len = 0;
const char *data = luaL_checklstring (L, 2, &len);
mi->update (dudat->ctx, data, len);
return 0; // No return value
}
/* Called as object, no params. Returns digest of default size. */
static int crypto_hash_finalize (lua_State *L)
{
NODE_DBG("enter crypto_hash_update.\n");
digest_user_datum_t *dudat;
size_t sl;
dudat = (digest_user_datum_t *)luaL_checkudata(L, 1, "crypto.hash");
const digest_mech_info_t *mi = dudat->mech_info;
uint8_t digest[mi->digest_size]; // Allocate as local
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;
}
static sint32_t vfs_read_wrap (int fd, void *ptr, size_t len)
{
return vfs_read (fd, ptr, len);
}
/* rawdigest = crypto.hash("MD5", filename)
* strdigest = crypto.toHex(rawdigest)
*/
static int crypto_flhash (lua_State *L)
{
const digest_mech_info_t *mi = crypto_digest_mech (luaL_checkstring (L, 1));
if (!mi)
return bad_mech (L);
const char *filename = luaL_checkstring (L, 2);
// Open the file
int file_fd = vfs_open (filename, "r");
if(!file_fd) {
return bad_file(L);
}
// Compute hash
uint8_t digest[mi->digest_size];
int returncode = crypto_fhash (mi, &vfs_read_wrap, file_fd, digest);
// Finish up
vfs_close(file_fd);
if (returncode == ENOMEM)
return bad_mem (L);
else if (returncode == EINVAL)
return bad_mech(L);
else
lua_pushlstring (L, digest, sizeof (digest));
return 1;
}
/* rawsignature = crypto.hmac("SHA1", str, key)
* strsignature = crypto.toHex(rawsignature)
*/
static int crypto_lhmac (lua_State *L)
{
const digest_mech_info_t *mi = crypto_digest_mech (luaL_checkstring (L, 1));
if (!mi)
return bad_mech (L);
size_t len = 0;
const char *data = luaL_checklstring (L, 2, &len);
size_t klen = 0;
const char *key = luaL_checklstring (L, 3, &klen);
uint8_t digest[mi->digest_size];
if (crypto_hmac (mi, data, len, key, klen, digest) != 0)
return bad_mem (L);
lua_pushlstring (L, digest, sizeof (digest));
return 1;
}
static const crypto_mech_t *get_mech (lua_State *L, int idx)
{
const char *name = luaL_checkstring (L, idx);
const crypto_mech_t *mech = crypto_encryption_mech (name);
if (mech)
return mech;
luaL_error (L, "unknown cipher: %s", name);
__builtin_unreachable ();
}
static int crypto_encdec (lua_State *L, bool enc)
{
const crypto_mech_t *mech = get_mech (L, 1);
size_t klen, dlen, ivlen, bs = mech->block_size;
const char *key = luaL_checklstring (L, 2, &klen);
const char *data = luaL_checklstring (L, 3, &dlen);
const char *iv = luaL_optlstring (L, 4, "", &ivlen);
size_t outlen = ((dlen + bs -1) / bs) * bs;
char *buf = luaM_newvector(L, outlen, char);
crypto_op_t op = {
key, klen,
iv, ivlen,
data, dlen,
buf, outlen,
enc ? OP_ENCRYPT : OP_DECRYPT
};
int status = mech->run (&op);
lua_pushlstring (L, buf, outlen); /* discarded on error but what the hell */
luaM_freearray(L, buf, outlen, char);
return status ? 1 : luaL_error (L, "crypto op failed");
}
static int lcrypto_encrypt (lua_State *L)
{
return crypto_encdec (L, true);
}
static int lcrypto_decrypt (lua_State *L)
{
return crypto_encdec (L, false);
}
// Hash function map
LROT_BEGIN(crypto_hash)
LROT_FUNCENTRY( update, crypto_hash_update )
LROT_FUNCENTRY( finalize, crypto_hash_finalize )
LROT_TABENTRY( __index, crypto_hash )
LROT_END( crypto_hash, crypto_hash, LROT_MASK_INDEX )
// Module function map
LROT_BEGIN(crypto)
LROT_FUNCENTRY( sha1, crypto_sha1 )
LROT_FUNCENTRY( toBase64, crypto_base64_encode )
LROT_FUNCENTRY( toHex, crypto_hex_encode )
LROT_FUNCENTRY( mask, crypto_mask )
LROT_FUNCENTRY( hash, crypto_lhash )
LROT_FUNCENTRY( fhash, crypto_flhash )
LROT_FUNCENTRY( new_hash, crypto_new_hash )
LROT_FUNCENTRY( hmac, crypto_lhmac )
LROT_FUNCENTRY( new_hmac, crypto_new_hmac )
LROT_FUNCENTRY( encrypt, lcrypto_encrypt )
LROT_FUNCENTRY( decrypt, lcrypto_decrypt )
LROT_END( crypto, NULL, 0 )
int luaopen_crypto ( lua_State *L )
{
luaL_rometatable(L, "crypto.hash", LROT_TABLEREF(crypto_hash));
return 0;
}
NODEMCU_MODULE(CRYPTO, "crypto", crypto, luaopen_crypto);