Browse Source

Added crypto.fhash function for hashing files

cheloftus 8 years ago
parent
commit
58dd15e1d1
4 changed files with 108 additions and 0 deletions
  1. 33 0
      app/crypto/digests.c
  2. 11 0
      app/crypto/digests.h
  3. 38 0
      app/modules/crypto.c
  4. 26 0
      docs/en/modules/crypto.md

+ 33 - 0
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,

+ 11 - 0
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.

+ 38 - 0
app/modules/crypto.c

@@ -1,10 +1,12 @@
 // Module for cryptography
 
+#include <c_errno.h>
 #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 ) },

+ 26 - 0
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.