瀏覽代碼

Added AES support to crypto module.

Merely a wrapper around the (undocumented) internal SDK AES functions.
Johny Mattsson 8 年之前
父節點
當前提交
5b381d2f1e
共有 5 個文件被更改,包括 300 次插入0 次删除
  1. 131 0
      app/crypto/mech.c
  2. 30 0
      app/crypto/mech.h
  3. 14 0
      app/crypto/sdk-aes.h
  4. 66 0
      app/modules/crypto.c
  5. 59 0
      docs/en/modules/crypto.md

+ 131 - 0
app/crypto/mech.c

@@ -0,0 +1,131 @@
+/*
+ * Copyright 2016 Dius Computing Pty Ltd. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the
+ *   distribution.
+ * - Neither the name of the copyright holders nor the names of
+ *   its contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Johny Mattsson <jmattsson@dius.com.au>
+ */
+
+#include "mech.h"
+#include "sdk-aes.h"
+#include "c_string.h"
+
+/* ----- AES ---------------------------------------------------------- */
+
+static const struct aes_funcs
+{
+  void *(*init) (const char *key, size_t keylen);
+  void (*crypt) (void *ctx, const char *in, char *out);
+  void (*deinit) (void *ctx);
+} aes_funcs[] =
+{
+  { aes_encrypt_init, aes_encrypt, aes_encrypt_deinit },
+  { aes_decrypt_init, aes_decrypt, aes_decrypt_deinit }
+};
+
+static bool do_aes (crypto_op_t *co, bool with_cbc)
+{
+  const struct aes_funcs *funcs = &aes_funcs[co->op];
+
+  void *ctx = funcs->init (co->key, co->keylen);
+  if (!ctx)
+    return false;
+
+  char iv[AES_BLOCKSIZE] = { 0 };
+  if (with_cbc && co->ivlen)
+    c_memcpy (iv, co->iv, co->ivlen < AES_BLOCKSIZE ? co->ivlen : AES_BLOCKSIZE);
+
+  const char *src = co->data;
+  char *dst = co->out;
+
+  size_t left = co->datalen;
+  while (left)
+  {
+    char block[AES_BLOCKSIZE] = { 0 };
+    size_t n = left > AES_BLOCKSIZE ? AES_BLOCKSIZE : left;
+    c_memcpy (block, src, n);
+
+    if (with_cbc && co->op == OP_ENCRYPT)
+    {
+      const char *xor = (src == co->data) ? iv : dst - AES_BLOCKSIZE;
+      int i;
+      for (i = 0; i < AES_BLOCKSIZE; ++i)
+        block[i] ^= xor[i];
+    }
+
+    funcs->crypt (ctx, block, dst);
+
+    if (with_cbc && co->op == OP_DECRYPT)
+    {
+      const char *xor = (src == co->data) ? iv : src - AES_BLOCKSIZE;
+      int i;
+      for (i = 0; i < AES_BLOCKSIZE; ++i)
+        dst[i] ^= xor[i];
+    }
+
+    left -= n;
+    src += n;
+    dst += n;
+  }
+
+  funcs->deinit (ctx);
+  return true;
+}
+
+
+static bool do_aes_ecb (crypto_op_t *co)
+{
+  return do_aes (co, false);
+}
+
+static bool do_aes_cbc (crypto_op_t *co)
+{
+  return do_aes (co, true);
+}
+
+
+/* ----- mechs -------------------------------------------------------- */
+
+static const crypto_mech_t mechs[] =
+{
+  { "AES-ECB",  do_aes_ecb, AES_BLOCKSIZE },
+  { "AES-CBC",  do_aes_cbc, AES_BLOCKSIZE }
+};
+
+
+const crypto_mech_t *crypto_encryption_mech (const char *name)
+{
+  size_t i;
+  for (i = 0; i < sizeof (mechs) / sizeof (mechs[0]); ++i)
+  {
+    const crypto_mech_t *mech = mechs + i;
+    if (strcasecmp (name, mech->name) == 0)
+      return mech;
+  }
+  return 0;
+}

+ 30 - 0
app/crypto/mech.h

@@ -0,0 +1,30 @@
+#ifndef _MECH_H_
+#define _MECH_H_
+
+#include "c_types.h"
+
+typedef struct
+{
+  const char *key;
+  size_t keylen;
+  const char *iv;
+  size_t ivlen;
+  const char *data;
+  size_t datalen;
+  char *out;
+  size_t outlen;
+  enum { OP_ENCRYPT, OP_DECRYPT } op;
+} crypto_op_t;
+
+
+typedef struct
+{
+  const char *name;
+  bool (*run) (crypto_op_t *op);
+  uint16_t block_size;
+} crypto_mech_t;
+
+
+const crypto_mech_t *crypto_encryption_mech (const char *name);
+
+#endif

+ 14 - 0
app/crypto/sdk-aes.h

@@ -0,0 +1,14 @@
+#ifndef _SDK_AES_H_
+#define _SDK_AES_H_
+
+#define AES_BLOCKSIZE 16
+
+void *aes_encrypt_init (const char *key, size_t len);
+void aes_encrypt (void *ctx, const char *plain, char *crypt);
+void aes_encrypt_deinit (void *ctx);
+
+void *aes_decrypt_init (const char *key, size_t len);
+void aes_decrypt (void *ctx, const char *crypt, char *plain);
+void aes_decrypt_deinit (void *ctx);
+
+#endif

+ 66 - 0
app/modules/crypto.c

@@ -6,6 +6,7 @@
 #include "c_types.h"
 #include "c_stdlib.h"
 #include "../crypto/digests.h"
+#include "../crypto/mech.h"
 
 #include "user_interface.h"
 
@@ -149,6 +150,69 @@ static int crypto_lhmac (lua_State *L)
 }
 
 
+
+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;
+  const char *key = luaL_checklstring (L, 2, &klen);
+  size_t dlen;
+  const char *data = luaL_checklstring (L, 3, &dlen);
+
+  size_t ivlen;
+  const char *iv = luaL_optlstring (L, 4, "", &ivlen);
+
+  size_t bs = mech->block_size;
+  size_t outlen = ((dlen + bs -1) / bs) * bs;
+  char *buf = (char *)os_zalloc (outlen);
+  if (!buf)
+    return luaL_error (L, "crypto init failed");
+
+  crypto_op_t op =
+  {
+    key, klen,
+    iv, ivlen,
+    data, dlen,
+    buf, outlen,
+    enc ? OP_ENCRYPT : OP_DECRYPT
+  };
+  if (!mech->run (&op))
+  {
+    os_free (buf);
+    return luaL_error (L, "crypto op failed");
+  }
+  else
+  {
+    lua_pushlstring (L, buf, outlen);
+    // note: if lua_pushlstring runs out of memory, we leak buf :(
+    os_free (buf);
+    return 1;
+  }
+}
+
+static int lcrypto_encrypt (lua_State *L)
+{
+  return crypto_encdec (L, true);
+}
+
+static int lcrypto_decrypt (lua_State *L)
+{
+  return crypto_encdec (L, false);
+}
+
+
 // Module function map
 static const LUA_REG_TYPE crypto_map[] = {
   { LSTRKEY( "sha1" ),     LFUNCVAL( crypto_sha1 ) },
@@ -157,6 +221,8 @@ static const LUA_REG_TYPE crypto_map[] = {
   { LSTRKEY( "mask" ),     LFUNCVAL( crypto_mask ) },
   { LSTRKEY( "hash"   ),   LFUNCVAL( crypto_lhash ) },
   { LSTRKEY( "hmac"   ),   LFUNCVAL( crypto_lhmac ) },
+  { LSTRKEY( "encrypt" ),  LFUNCVAL( lcrypto_encrypt ) },
+  { LSTRKEY( "decrypt" ),  LFUNCVAL( lcrypto_decrypt ) },
   { LNILKEY, LNILVAL }
 };
 

+ 59 - 0
docs/en/modules/crypto.md

@@ -2,6 +2,65 @@
 
 The crypto modules provides various functions for working with cryptographic algorithms.
 
+## crypto.encrypt()
+
+Encrypts Lua strings.
+
+#### Syntax
+`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
+  - `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
+
+#### Returns
+The encrypted data as a binary string. For AES this is always a multiple of 16 bytes in length.
+
+#### Example
+```lua
+print(crypto.toHex(crypto.encrypt("AES-ECB", "1234567890abcdef", "Hi, I'm secret!")))
+```
+
+#### See also
+  - [`crypto.decrypt()`](#cryptodecrypt)
+
+
+## crypto.decrypt()
+
+Decrypts previously encrypted data.
+
+#### Syntax
+`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
+  - `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
+
+#### Returns
+The decrypted string.
+
+Note that the decrypted string may contain extra zero-bytes of padding at the end. One way of stripping such padding is to use `:match("(.-)%z*$")` on the decrypted string. Additional care needs to be taken if working on binary data, in which case the real length likely needs to be encoded with the data, and at which point `:sub(1, n)` can be used to strip the padding.
+
+#### Example
+```lua
+key = "1234567890abcdef"
+cipher = crypto.encrypt("AES-ECB", key, "Hi, I'm secret!")
+print(crypto.toHex(cipher))
+print(crypto.decrypt("AES-ECB", key, cipher))
+```
+
+#### See also
+  - [`crypto.encrypt()`](#cryptoencrypt)
+
+
 ## crypto.hash()
 
 Compute a cryptographic hash of a Lua string.