diff --git a/app/crypto/digests.c b/app/crypto/digests.c index 6c779365..d3d965e8 100644 --- a/app/crypto/digests.c +++ b/app/crypto/digests.c @@ -122,6 +122,39 @@ int ICACHE_FLASH_ATTR crypto_hash (const digest_mech_info_t *mi, } +int ICACHE_FLASH_ATTR crypto_fhash (const digest_mech_info_t *mi, + read_fn read, int readarg, + uint8_t *digest) +{ + if (!mi) + return EINVAL; + + // Initialise + void *ctx = (void *)os_malloc (mi->ctx_size); + if (!ctx) + return ENOMEM; + mi->create (ctx); + + // Hash bytes from file in blocks + uint8_t* buffer = (uint8_t*)os_malloc (mi->block_size); + if (!buffer) + return ENOMEM; + + int read_len = 0; + do { + read_len = read(readarg, buffer, mi->block_size); + mi->update (ctx, buffer, read_len); + } while (read_len == mi->block_size); + + // Finish up + mi->finalize (digest, ctx); + + os_free (buffer); + os_free (ctx); + return 0; +} + + 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, diff --git a/app/crypto/digests.h b/app/crypto/digests.h index 05700701..bb6fe554 100644 --- a/app/crypto/digests.h +++ b/app/crypto/digests.h @@ -6,6 +6,7 @@ typedef void (*create_ctx_fn)(void *ctx); typedef void (*update_ctx_fn)(void *ctx, const uint8_t *msg, int len); typedef void (*finalize_ctx_fn)(uint8_t *digest, void *ctx); +typedef size_t ( *read_fn )(int fd, void *ptr, size_t len); /** * Description of a message digest mechanism. @@ -53,6 +54,16 @@ const digest_mech_info_t *crypto_digest_mech (const char *mech); */ int crypto_hash (const digest_mech_info_t *mi, const char *data, size_t data_len, uint8_t *digest); +/** + * Wrapper function for performing a one-in-all hashing operation of a file. + * @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 read Pointer to the read function (e.g. fs_read) + * @param readarg Argument to pass to the read function (e.g. file descriptor) + * @param digest Output buffer, must be at least @c mi->digest_size in size. + * @return 0 on success, non-zero on error. + */ +int crypto_fhash (const digest_mech_info_t *mi, read_fn read, int readarg, uint8_t *digest); /** * Generate a HMAC signature. diff --git a/app/modules/crypto.c b/app/modules/crypto.c index 53760cb5..f3ae86f1 100644 --- a/app/modules/crypto.c +++ b/app/modules/crypto.c @@ -1,10 +1,12 @@ // Module for cryptography +#include #include "module.h" #include "lauxlib.h" #include "platform.h" #include "c_types.h" #include "c_stdlib.h" +#include "flash_fs.h" #include "../crypto/digests.h" #include "../crypto/mech.h" @@ -106,6 +108,7 @@ static int crypto_mask( lua_State* L ) 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) @@ -128,6 +131,40 @@ static int crypto_lhash (lua_State *L) } +/* 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 = fs_open (filename, FS_RDONLY); + if(file_fd < FS_OPEN_OK) { + return bad_file(L); + } + + // Compute hash + uint8_t digest[mi->digest_size]; + int returncode = crypto_fhash (mi, &fs_read, file_fd, digest); + + // Finish up + fs_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) */ @@ -220,6 +257,7 @@ static const LUA_REG_TYPE crypto_map[] = { { LSTRKEY( "toHex" ), LFUNCVAL( crypto_hex_encode ) }, { LSTRKEY( "mask" ), LFUNCVAL( crypto_mask ) }, { LSTRKEY( "hash" ), LFUNCVAL( crypto_lhash ) }, + { LSTRKEY( "fhash" ), LFUNCVAL( crypto_flhash ) }, { LSTRKEY( "hmac" ), LFUNCVAL( crypto_lhmac ) }, { LSTRKEY( "encrypt" ), LFUNCVAL( lcrypto_encrypt ) }, { LSTRKEY( "decrypt" ), LFUNCVAL( lcrypto_decrypt ) }, diff --git a/docs/en/modules/crypto.md b/docs/en/modules/crypto.md index 5bb913e3..db674862 100644 --- a/docs/en/modules/crypto.md +++ b/docs/en/modules/crypto.md @@ -61,6 +61,32 @@ print(crypto.decrypt("AES-ECB", key, cipher)) - [`crypto.encrypt()`](#cryptoencrypt) +## crypto.fhash() + +Compute a cryptographic hash of a a file. + +#### Syntax +`hash = crypto.fhash(algo, filename)` + +#### Parameters +- `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 ). + +#### Example +```lua +print(crypto.toHex(crypto.fhash("sha1","myfile.lua"))) +``` + ## crypto.hash() Compute a cryptographic hash of a Lua string.