Skip to content

Instantly share code, notes, and snippets.

@cryptid11
Created April 30, 2015 08:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cryptid11/89bf0c6cc3ad90b17445 to your computer and use it in GitHub Desktop.
Save cryptid11/89bf0c6cc3ad90b17445 to your computer and use it in GitHub Desktop.
/*
** $Id: lcrypto.c,v 1.2 2006/08/25 03:24:17 nezroy Exp $
** See Copyright Notice in license.html
*/
#include <string.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/dsa.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/x509.h>
#include <stddef.h>
#include <assert.h>
#ifndef MIN
# define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
#ifdef __GNUC__
# define UNUSED __attribute__ ((unused))
#else
# define UNUSED
#endif
#include "lua.h"
#include "lauxlib.h"
#include "lcrypto.h"
LUACRYPTO_API int luaopen_crypto(lua_State *L);
#if LUA_VERSION_NUM >= 502
#if !defined(LUA_COMPAT_MODULE)
static void luaL_register (lua_State *L, const char *libname, const luaL_Reg *l)
{
if (libname) lua_newtable(L);
luaL_setfuncs(L, l, 0);
}
#endif
#ifndef lua_objlen
#define lua_objlen lua_rawlen
#endif
#endif
static void luacrypto_register_submodule(lua_State *L, const char *libname, const luaL_Reg *l)
{
int top = lua_gettop(L);
const char *sname = strchr(libname, '.');
assert(lua_istable(L, -1));
assert(sname && sname[1]);
assert(NULL == strchr(&sname[1], '.'));
lua_getfield(L, -1, ++sname);
if (lua_istable(L, -1)) libname = NULL; else lua_pop(L, 1);
luaL_register(L, libname, l);
lua_setfield(L, -2, sname);
assert(top == lua_gettop(L));
}
static int crypto_error(lua_State *L)
{
char buf[120];
unsigned long e = ERR_get_error();
ERR_load_crypto_strings();
lua_pushnil(L);
lua_pushstring(L, ERR_error_string(e, buf));
ERR_clear_error();
return 2;
}
/*************** DIGEST API ***************/
static EVP_MD_CTX *digest_pnew(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)lua_newuserdata(L, sizeof(EVP_MD_CTX));
luaL_getmetatable(L, LUACRYPTO_DIGESTNAME);
lua_setmetatable(L, -2);
return c;
}
static int digest_fnew(lua_State *L)
{
const char *s = luaL_checkstring(L, 1);
const EVP_MD *digest = EVP_get_digestbyname(s);
EVP_MD_CTX *c;
if (digest == NULL)
return luaL_argerror(L, 1, "invalid digest/cipher type");
c = digest_pnew(L);
EVP_MD_CTX_init(c);
if (EVP_DigestInit_ex(c, digest, NULL) != 1)
return crypto_error(L);
return 1;
}
static int digest_clone(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_DIGESTNAME);
EVP_MD_CTX *d = digest_pnew(L);
EVP_MD_CTX_init(d);
if (!EVP_MD_CTX_copy_ex(d, c))
{
return crypto_error(L);
}
return 1;
}
static int digest_reset(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_DIGESTNAME);
const EVP_MD *t = EVP_MD_CTX_md(c);
if (!EVP_MD_CTX_cleanup(c))
{
return crypto_error(L);
}
EVP_MD_CTX_init(c);
if (!EVP_DigestInit_ex(c, t, NULL))
{
return crypto_error(L);
}
return 0;
}
static int digest_update(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_DIGESTNAME);
size_t slen;
const char *s = luaL_checklstring(L, 2, &slen);
if (!EVP_DigestUpdate(c, s, slen))
{
return crypto_error(L);
}
lua_settop(L, 1);
return 1;
}
static int digest_final(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_DIGESTNAME);
EVP_MD_CTX *d = NULL;
unsigned char digest[EVP_MAX_MD_SIZE];
unsigned int written = 0;
unsigned int i;
char *hex;
if (lua_isstring(L, 2))
{
size_t slen;
const char *s = luaL_checklstring(L, 2, &slen);
if (!EVP_DigestUpdate(c, s, slen))
{
return crypto_error(L);
}
}
d = EVP_MD_CTX_create();
if (!EVP_MD_CTX_copy_ex(d, c))
{
return crypto_error(L);
}
if (!EVP_DigestFinal_ex(d, digest, &written))
{
return crypto_error(L);
}
EVP_MD_CTX_destroy(d);
if (lua_toboolean(L, 3))
lua_pushlstring(L, (char *)digest, written);
else
{
hex = (char *)calloc(sizeof(char), written * 2 + 1);
for (i = 0; i < written; i++)
sprintf(hex + 2 * i, "%02x", digest[i]);
lua_pushlstring(L, hex, written * 2);
free(hex);
}
return 1;
}
static int digest_tostring(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_DIGESTNAME);
char s[64];
sprintf(s, "%s %p", LUACRYPTO_DIGESTNAME, (void *)c);
lua_pushstring(L, s);
return 1;
}
static int digest_gc(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_DIGESTNAME);
if (!EVP_MD_CTX_cleanup(c))
{
return crypto_error(L);
}
return 1;
}
static int digest_fdigest(lua_State *L)
{
const char *type_name = luaL_checkstring(L, 2);
const EVP_MD *type = EVP_get_digestbyname(type_name);
size_t slen;
const char *s = luaL_checklstring(L, 3, &slen);
unsigned char digest[EVP_MAX_MD_SIZE];
unsigned int written = 0;
EVP_MD_CTX *c;
if (type == NULL)
{
luaL_argerror(L, 1, "invalid digest type");
return 0;
}
c = EVP_MD_CTX_create();
if (!EVP_DigestInit_ex(c, type, NULL))
{
EVP_MD_CTX_destroy(c);
return crypto_error(L);
}
if (!EVP_DigestUpdate(c, s, slen))
{
EVP_MD_CTX_destroy(c);
return crypto_error(L);
}
if (!EVP_DigestFinal_ex(c, digest, &written))
{
EVP_MD_CTX_destroy(c);
return crypto_error(L);
}
EVP_MD_CTX_destroy(c);
if (lua_toboolean(L, 4))
lua_pushlstring(L, (char *)digest, written);
else
{
char *hex = (char *)calloc(sizeof(char), written * 2 + 1);
unsigned int i;
for (i = 0; i < written; i++)
sprintf(hex + 2 * i, "%02x", digest[i]);
lua_pushlstring(L, hex, written * 2);
free(hex);
}
return 1;
}
/*************** ENCRYPT API ***************/
static int parse_enc_params(lua_State *L, EVP_CIPHER **cipher, char **key, size_t *key_len,
char **iv, size_t *iv_len, int *pad, int *size_to_return,
int cipher_pos, int key_pos, int iv_pos, int pad_pos)
{
const char *s = luaL_checkstring(L, cipher_pos);
*cipher = (EVP_CIPHER *)EVP_get_cipherbyname(s);
*size_to_return = 0;
if (*cipher == NULL)
{
return luaL_argerror(L, cipher_pos, "invalid encrypt cipher");
}
*key_len = 0;
*key = (char *)luaL_checklstring(L, key_pos, key_len);
if (*key_len > EVP_MAX_KEY_LENGTH)
{
return luaL_argerror(L, key_pos, "invalid encrypt/decrypt key");
}
*iv_len = 0;
*iv = (char *)lua_tolstring(L, iv_pos, iv_len); /* can be NULL */
if (*iv && (*iv_len > (size_t)EVP_CIPHER_iv_length(*cipher)))
return luaL_argerror(L, iv_pos, "invalid iv length");
*pad = (lua_gettop(L) < pad_pos || lua_toboolean(L, pad_pos));
return 1;
}
static int parse_new_enc_params(lua_State *L, EVP_CIPHER **cipher, char **key, size_t *key_len,
char **iv, size_t *iv_len, int *pad, int *size_to_return)
{
return parse_enc_params(L, cipher, key, key_len, iv, iv_len, pad, size_to_return,
1, 2, 3, 4);
}
static int parse_f_enc_params(lua_State *L, EVP_CIPHER **cipher, char **key, size_t *key_len,
char **iv, size_t *iv_len, int *pad, int *size_to_return)
{
return parse_enc_params(L, cipher, key, key_len, iv, iv_len, pad, size_to_return,
2, 4, 5, 6);
}
#define TRY_CTX(fun) if (!fun) { *size_to_return = crypto_error(L); return 0; }
static int init_encryptor_decryptor(int (*init_fun)(EVP_CIPHER_CTX *, const EVP_CIPHER *, ENGINE *, const unsigned char *, const unsigned char *),
lua_State *L, EVP_CIPHER_CTX *c, const EVP_CIPHER *cipher, const char *key, size_t key_len,
const char *iv, size_t iv_len, int pad, int *size_to_return)
{
unsigned char the_key[EVP_MAX_KEY_LENGTH] = {0};
unsigned char the_iv[EVP_MAX_IV_LENGTH] = {0};
EVP_CIPHER_CTX_init(c);
TRY_CTX(init_fun(c, cipher, NULL, NULL, NULL))
if (!pad)
TRY_CTX(EVP_CIPHER_CTX_set_padding(c, 0))
if (iv)
memcpy(the_iv, iv, iv_len);
memcpy(the_key, key, key_len);
TRY_CTX(init_fun(c, NULL, NULL, the_key, the_iv))
return 1;
}
static EVP_CIPHER_CTX *encrypt_pnew(lua_State *L)
{
EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)lua_newuserdata(L, sizeof(EVP_CIPHER_CTX));
luaL_getmetatable(L, LUACRYPTO_ENCRYPTNAME);
lua_setmetatable(L, -2);
return c;
}
static int encrypt_fnew(lua_State *L)
{
const char *key = 0, *iv = 0;
const EVP_CIPHER *cipher = 0;
size_t key_len = 0, iv_len = 0;
int pad = 1, size_to_return = 0;
EVP_CIPHER_CTX *c;
if (!parse_new_enc_params(L, (EVP_CIPHER **)&cipher, (char **)&key, &key_len,
(char **)&iv, &iv_len, &pad, &size_to_return))
{
return size_to_return;
}
c = encrypt_pnew(L);
if (!init_encryptor_decryptor(EVP_EncryptInit_ex, L, c, cipher, key, key_len, iv, iv_len, pad, &size_to_return))
{
return size_to_return;
}
return 1;
}
static int encrypt_update(lua_State *L)
{
EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)luaL_checkudata(L, 1, LUACRYPTO_ENCRYPTNAME);
size_t input_len = 0;
const unsigned char *input = (unsigned char *)luaL_checklstring(L, 2, &input_len);
int output_len = 0;
unsigned char *buffer = NULL;
buffer = (unsigned char *)malloc(input_len + (size_t)EVP_CIPHER_CTX_block_size(c));
if (!EVP_EncryptUpdate(c, buffer, &output_len, input, (int)input_len))
{
free(buffer);
return crypto_error(L);
}
lua_pushlstring(L, (char *)buffer, (size_t)output_len);
free(buffer);
return 1;
}
static int encrypt_final(lua_State *L)
{
EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)luaL_checkudata(L, 1, LUACRYPTO_ENCRYPTNAME);
int output_len = 0;
unsigned char buffer[EVP_MAX_BLOCK_LENGTH];
if (!EVP_EncryptFinal_ex(c, buffer, &output_len))
{
return crypto_error(L);
}
lua_pushlstring(L, (char *)buffer, (size_t)output_len);
return 1;
}
static int encrypt_tostring(lua_State *L)
{
EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)luaL_checkudata(L, 1, LUACRYPTO_ENCRYPTNAME);
char s[64];
sprintf(s, "%s %p", LUACRYPTO_ENCRYPTNAME, (void *)c);
lua_pushstring(L, s);
return 1;
}
static int encrypt_gc(lua_State *L)
{
EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)luaL_checkudata(L, 1, LUACRYPTO_ENCRYPTNAME);
if (!EVP_CIPHER_CTX_cleanup(c))
{
return crypto_error(L);
}
return 1;
}
static int encrypt_fencrypt(lua_State *L)
{
/* parameter 1 is the 'crypto.encrypt' table */
const EVP_CIPHER *type;
size_t input_len = 0;
const unsigned char *input = (unsigned char *)luaL_checklstring(L, 3, &input_len);
size_t key_len = 0, iv_len = 0;
const char *key = NULL, *iv = NULL;
int pad = 0, size_to_return = 0;
EVP_CIPHER_CTX c;
int output_len = 0;
int len = 0;
unsigned char *buffer;
if (!parse_f_enc_params(L, (EVP_CIPHER **)&type, (char **)&key, &key_len, (char **)&iv, &iv_len, &pad, &size_to_return))
{
return size_to_return;
}
if (!init_encryptor_decryptor(EVP_EncryptInit_ex, L, &c, type, key, key_len, iv, iv_len, pad, &size_to_return))
{
return size_to_return;
}
buffer = (unsigned char *)malloc(input_len + (size_t)EVP_CIPHER_CTX_block_size(&c));
if (!EVP_EncryptUpdate(&c, buffer, &len, input, (int)input_len))
{
EVP_CIPHER_CTX_cleanup(&c);
free(buffer);
return crypto_error(L);
}
output_len += len;
if (!EVP_EncryptFinal_ex(&c, &buffer[output_len], &len))
{
EVP_CIPHER_CTX_cleanup(&c);
free(buffer);
return crypto_error(L);
}
output_len += len;
lua_pushlstring(L, (char *)buffer, (size_t)output_len);
free(buffer);
EVP_CIPHER_CTX_cleanup(&c);
return 1;
}
/*************** DECRYPT API ***************/
static EVP_CIPHER_CTX *decrypt_pnew(lua_State *L)
{
EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)lua_newuserdata(L, sizeof(EVP_CIPHER_CTX));
luaL_getmetatable(L, LUACRYPTO_DECRYPTNAME);
lua_setmetatable(L, -2);
return c;
}
static int decrypt_fnew(lua_State *L)
{
const char *key = 0, *iv = 0;
const EVP_CIPHER *cipher = 0;
size_t key_len = 0, iv_len = 0;
int pad = 1, size_to_return = 0;
EVP_CIPHER_CTX *c;
if (!parse_new_enc_params(L, (EVP_CIPHER **)&cipher, (char **)&key, &key_len,
(char **)&iv, &iv_len, &pad, &size_to_return))
{
return size_to_return;
}
c = decrypt_pnew(L);
if (!init_encryptor_decryptor(EVP_DecryptInit_ex, L, c, cipher, key, key_len, iv, iv_len, pad, &size_to_return))
{
return size_to_return;
}
return 1;
}
static int decrypt_update(lua_State *L)
{
EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)luaL_checkudata(L, 1, LUACRYPTO_DECRYPTNAME);
size_t input_len = 0;
const unsigned char *input = (unsigned char *)luaL_checklstring(L, 2, &input_len);
int output_len = 0;
unsigned char *buffer = NULL;
buffer = (unsigned char *)malloc(input_len + (size_t)EVP_CIPHER_CTX_block_size(c));
if (!EVP_DecryptUpdate(c, buffer, &output_len, input, (int)input_len))
{
return crypto_error(L);
}
lua_pushlstring(L, (char *)buffer, (size_t)output_len);
free(buffer);
return 1;
}
static int decrypt_final(lua_State *L)
{
EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)luaL_checkudata(L, 1, LUACRYPTO_DECRYPTNAME);
int output_len = 0;
unsigned char buffer[EVP_MAX_BLOCK_LENGTH];
if (!EVP_DecryptFinal_ex(c, buffer, &output_len))
{
return crypto_error(L);
}
lua_pushlstring(L, (char *)buffer, (size_t)output_len);
return 1;
}
static int decrypt_tostring(lua_State *L)
{
EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)luaL_checkudata(L, 1, LUACRYPTO_DECRYPTNAME);
char s[64];
sprintf(s, "%s %p", LUACRYPTO_DECRYPTNAME, (void *)c);
lua_pushstring(L, s);
return 1;
}
static int decrypt_gc(lua_State *L)
{
EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)luaL_checkudata(L, 1, LUACRYPTO_DECRYPTNAME);
if (!EVP_CIPHER_CTX_cleanup(c))
{
return crypto_error(L);
}
return 1;
}
static int decrypt_fdecrypt(lua_State *L)
{
/* parameter 1 is the 'crypto.decrypt' table */
size_t input_len = 0;
const unsigned char *input = (unsigned char *)luaL_checklstring(L, 3, &input_len);
const EVP_CIPHER *type;
size_t key_len = 0, iv_len = 0;
const char *key = NULL, *iv = NULL;
int pad = 0, size_to_return = 0;
EVP_CIPHER_CTX c;
unsigned char *buffer;
int output_len = 0;
int len = 0;
if (!parse_f_enc_params(L, (EVP_CIPHER **)&type, (char **)&key, &key_len, (char **)&iv, &iv_len, &pad, &size_to_return))
{
return size_to_return;
}
if (!init_encryptor_decryptor(EVP_DecryptInit_ex, L, &c, type, key, key_len, iv, iv_len, pad, &size_to_return))
{
return size_to_return;
}
buffer = (unsigned char *)malloc(input_len + (size_t)EVP_CIPHER_CTX_block_size(&c));
if (!EVP_DecryptUpdate(&c, buffer, &len, input, (int)input_len))
{
EVP_CIPHER_CTX_cleanup(&c);
free(buffer);
return crypto_error(L);
}
output_len += len;
if (!EVP_DecryptFinal_ex(&c, &buffer[len], &len))
{
EVP_CIPHER_CTX_cleanup(&c);
free(buffer);
return crypto_error(L);
}
output_len += len;
lua_pushlstring(L, (char *)buffer, (size_t)output_len);
free(buffer);
EVP_CIPHER_CTX_cleanup(&c);
return 1;
}
/*************** HMAC API ***************/
static HMAC_CTX *hmac_pnew(lua_State *L)
{
HMAC_CTX *c = (HMAC_CTX *)lua_newuserdata(L, sizeof(HMAC_CTX));
luaL_getmetatable(L, LUACRYPTO_HMACNAME);
lua_setmetatable(L, -2);
return c;
}
static int hmac_fnew(lua_State *L)
{
HMAC_CTX *c = hmac_pnew(L);
const char *s = luaL_checkstring(L, 1);
size_t klen;
const char *k = luaL_checklstring(L, 2, &klen);
const EVP_MD *type = EVP_get_digestbyname(s);
if (type == NULL)
return luaL_argerror(L, 1, "invalid digest type");
HMAC_CTX_init(c);
HMAC_Init_ex(c, k, (int)klen, type, NULL);
return 1;
}
static int hmac_clone(lua_State *L)
{
HMAC_CTX *c = (HMAC_CTX *)luaL_checkudata(L, 1, LUACRYPTO_HMACNAME);
HMAC_CTX *d = hmac_pnew(L);
*d = *c;
return 1;
}
static int hmac_reset(lua_State *L)
{
HMAC_CTX *c = (HMAC_CTX *)luaL_checkudata(L, 1, LUACRYPTO_HMACNAME);
HMAC_Init_ex(c, NULL, 0, NULL, NULL);
return 0;
}
static int hmac_update(lua_State *L)
{
HMAC_CTX *c = (HMAC_CTX *)luaL_checkudata(L, 1, LUACRYPTO_HMACNAME);
size_t slen;
const char *s = luaL_checklstring(L, 2, &slen);
HMAC_Update(c, (unsigned char *)s, slen);
lua_settop(L, 1);
return 1;
}
static int hmac_final(lua_State *L)
{
HMAC_CTX *c = (HMAC_CTX *)luaL_checkudata(L, 1, LUACRYPTO_HMACNAME);
unsigned char digest[EVP_MAX_MD_SIZE];
unsigned int written = 0;
unsigned int i;
char *hex;
if (lua_isstring(L, 2))
{
size_t slen;
const char *s = luaL_checklstring(L, 2, &slen);
HMAC_Update(c, (unsigned char *)s, slen);
}
HMAC_Final(c, digest, &written);
if (lua_toboolean(L, 3))
lua_pushlstring(L, (char *)digest, written);
else
{
hex = (char *)calloc(sizeof(char), written * 2 + 1);
for (i = 0; i < written; i++)
sprintf(hex + 2 * i, "%02x", digest[i]);
lua_pushlstring(L, hex, written * 2);
free(hex);
}
return 1;
}
static int hmac_tostring(lua_State *L)
{
HMAC_CTX *c = (HMAC_CTX *)luaL_checkudata(L, 1, LUACRYPTO_HMACNAME);
char s[64];
sprintf(s, "%s %p", LUACRYPTO_HMACNAME, (void *)c);
lua_pushstring(L, s);
return 1;
}
static int hmac_gc(lua_State *L)
{
HMAC_CTX *c = (HMAC_CTX *)luaL_checkudata(L, 1, LUACRYPTO_HMACNAME);
HMAC_CTX_cleanup(c);
return 1;
}
static int hmac_fdigest(lua_State *L)
{
const char *t = luaL_checkstring(L, 1);
const EVP_MD *type = EVP_get_digestbyname(t);
size_t slen; const char *s;
size_t klen; const char *k;
unsigned char digest[EVP_MAX_MD_SIZE];
unsigned int written = 0;
unsigned int i;
char *hex;
HMAC_CTX c;
if (type == NULL)
{
luaL_argerror(L, 1, "invalid digest type");
return 0;
}
s = luaL_checklstring(L, 2, &slen);
k = luaL_checklstring(L, 3, &klen);
HMAC_CTX_init(&c);
HMAC_Init_ex(&c, k, klen, type, NULL);
HMAC_Update(&c, (unsigned char *)s, slen);
HMAC_Final(&c, digest, &written);
HMAC_CTX_cleanup(&c);
if (lua_toboolean(L, 4))
lua_pushlstring(L, (char *)digest, written);
else
{
hex = (char *)calloc(sizeof(char), written * 2 + 1);
for (i = 0; i < written; i++)
sprintf(hex + 2 * i, "%02x", digest[i]);
lua_pushlstring(L, hex, written * 2);
free(hex);
}
return 1;
}
/*************** SIGN API ***************/
static EVP_MD_CTX *sign_pnew(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)lua_newuserdata(L, sizeof(EVP_MD_CTX));
luaL_getmetatable(L, LUACRYPTO_SIGNNAME);
lua_setmetatable(L, -2);
return c;
}
static int sign_fnew(lua_State *L)
{
const char *s = luaL_checkstring(L, 1);
const EVP_MD *md = EVP_get_digestbyname(s);
EVP_MD_CTX *c;
if (md == NULL)
return luaL_argerror(L, 1, "invalid digest type");
c = sign_pnew(L);
EVP_MD_CTX_init(c);
if (EVP_SignInit_ex(c, md, NULL) != 1)
return crypto_error(L);
return 1;
}
static int sign_update(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_SIGNNAME);
size_t input_len = 0;
const unsigned char *input = (unsigned char *)luaL_checklstring(L, 2, &input_len);
EVP_SignUpdate(c, input, input_len);
return 0;
}
static int sign_final(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_SIGNNAME);
unsigned int output_len = 0;
unsigned char *buffer;
EVP_PKEY **pkey = (EVP_PKEY **)luaL_checkudata(L, 2, LUACRYPTO_PKEYNAME);
buffer = (unsigned char *)malloc((size_t)EVP_PKEY_size(*pkey));
if (!EVP_SignFinal(c, buffer, &output_len, *pkey))
{
free(buffer);
return crypto_error(L);
}
lua_pushlstring(L, (char *)buffer, output_len);
free(buffer);
return 1;
}
static int sign_tostring(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_SIGNNAME);
char s[64];
sprintf(s, "%s %p", LUACRYPTO_SIGNNAME, (void *)c);
lua_pushstring(L, s);
return 1;
}
static int sign_gc(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_SIGNNAME);
EVP_MD_CTX_cleanup(c);
return 1;
}
static int sign_fsign(lua_State *L)
{
/* parameter 1 is the 'crypto.sign' table */
const char *type_name = luaL_checkstring(L, 2);
const EVP_MD *type = EVP_get_digestbyname(type_name);
if (type == NULL)
{
luaL_argerror(L, 2, "invalid digest type");
return 0;
}
else
{
EVP_MD_CTX c;
size_t input_len = 0;
const unsigned char *input = (unsigned char *)luaL_checklstring(L, 3, &input_len);
unsigned int output_len = 0;
unsigned char *buffer = NULL;
EVP_PKEY **pkey = (EVP_PKEY **)luaL_checkudata(L, 4, LUACRYPTO_PKEYNAME);
EVP_MD_CTX_init(&c);
EVP_SignInit_ex(&c, type, NULL);
buffer = (unsigned char *)malloc((size_t)EVP_PKEY_size(*pkey));
EVP_SignUpdate(&c, input, input_len);
if (!EVP_SignFinal(&c, buffer, &output_len, *pkey))
{
EVP_MD_CTX_cleanup(&c);
free(buffer);
return crypto_error(L);
}
EVP_MD_CTX_cleanup(&c);
lua_pushlstring(L, (char *)buffer, output_len);
free(buffer);
return 1;
}
}
/*************** VERIFY API ***************/
static EVP_MD_CTX *verify_pnew(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)lua_newuserdata(L, sizeof(EVP_MD_CTX));
luaL_getmetatable(L, LUACRYPTO_VERIFYNAME);
lua_setmetatable(L, -2);
return c;
}
static int verify_fnew(lua_State *L)
{
const char *s = luaL_checkstring(L, 1);
const EVP_MD *md = EVP_get_digestbyname(s);
EVP_MD_CTX *c;
if (md == NULL)
return luaL_argerror(L, 1, "invalid digest type");
c = verify_pnew(L);
EVP_MD_CTX_init(c);
if (EVP_VerifyInit_ex(c, md, NULL) != 1)
return crypto_error(L);
return 1;
}
static int verify_update(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_VERIFYNAME);
size_t input_len = 0;
const unsigned char *input = (unsigned char *)luaL_checklstring(L, 2, &input_len);
if (EVP_VerifyUpdate(c, input, input_len) != 1)
return crypto_error(L);
return 0;
}
static int verify_final(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_VERIFYNAME);
size_t sig_len = 0;
const unsigned char *sig = (unsigned char *)luaL_checklstring(L, 2, &sig_len);
EVP_PKEY **pkey = (EVP_PKEY **)luaL_checkudata(L, 3, LUACRYPTO_PKEYNAME);
int ret;
ret = EVP_VerifyFinal(c, sig, sig_len, *pkey);
if (ret == -1)
return crypto_error(L);
else if (ret == 0)
ERR_clear_error();
lua_pushboolean(L, ret);
return 1;
}
static int verify_tostring(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_VERIFYNAME);
char s[64];
sprintf(s, "%s %p", LUACRYPTO_VERIFYNAME, (void *)c);
lua_pushstring(L, s);
return 1;
}
static int verify_gc(lua_State *L)
{
EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_VERIFYNAME);
EVP_MD_CTX_cleanup(c);
return 1;
}
static int verify_fverify(lua_State *L)
{
/* parameter 1 is the 'crypto.verify' table */
const char *type_name = luaL_checkstring(L, 2);
const EVP_MD *type = EVP_get_digestbyname(type_name);
if (type == NULL)
{
luaL_argerror(L, 1, "invalid digest type");
return 0;
}
else
{
EVP_MD_CTX c;
size_t input_len = 0;
const unsigned char *input = (unsigned char *)luaL_checklstring(L, 3, &input_len);
size_t sig_len = 0;
const unsigned char *sig = (unsigned char *)luaL_checklstring(L, 4, &sig_len);
EVP_PKEY **pkey = (EVP_PKEY **)luaL_checkudata(L, 5, LUACRYPTO_PKEYNAME);
int ret;
EVP_MD_CTX_init(&c);
if (EVP_VerifyInit_ex(&c, type, NULL) != 1)
return crypto_error(L);
if (EVP_VerifyUpdate(&c, input, input_len) != 1)
return crypto_error(L);
ret = EVP_VerifyFinal(&c, sig, sig_len, *pkey);
if (ret == -1)
return crypto_error(L);
else if (ret == 0)
ERR_clear_error();
EVP_MD_CTX_cleanup(&c);
lua_pushboolean(L, ret);
return 1;
}
}
/*************** RAND API ***************/
static int rand_do_bytes(lua_State *L, int (*bytes)(unsigned char *, int))
{
size_t count = (size_t)luaL_checkint(L, 1);
unsigned char tmp[256], *buf = tmp;
if (count > sizeof tmp)
buf = (unsigned char *)malloc(count);
if (!buf)
return luaL_error(L, "out of memory");
else if (!bytes(buf, (int)count))
return crypto_error(L);
lua_pushlstring(L, (char *)buf, count);
if (buf != tmp)
free(buf);
return 1;
}
static int rand_bytes(lua_State *L)
{
return rand_do_bytes(L, RAND_bytes);
}
static int rand_pseudo_bytes(lua_State *L)
{
return rand_do_bytes(L, RAND_pseudo_bytes);
}
static int rand_add(lua_State *L)
{
size_t num;
const void *buf = luaL_checklstring(L, 1, &num);
double entropy = luaL_optnumber(L, 2, num);
RAND_add(buf, (int)num, entropy);
return 0;
}
static int rand_status(lua_State *L)
{
lua_pushboolean(L, RAND_status());
return 1;
}
enum { WRITE_FILE_COUNT = 1024 };
static int rand_load(lua_State *L)
{
const char *name = luaL_optstring(L, 1, 0);
char tmp[256];
int n;
if (!name && !(name = RAND_file_name(tmp, sizeof tmp)))
return crypto_error(L);
n = RAND_load_file(name, WRITE_FILE_COUNT);
if (n == 0)
return crypto_error(L);
lua_pushnumber(L, n);
return 1;
}
static int rand_write(lua_State *L)
{
const char *name = luaL_optstring(L, 1, 0);
char tmp[256];
int n;
if (!name && !(name = RAND_file_name(tmp, sizeof tmp)))
return crypto_error(L);
n = RAND_write_file(name);
if (n == 0)
return crypto_error(L);
lua_pushnumber(L, n);
return 1;
}
static int rand_cleanup(lua_State *L UNUSED )
{
RAND_cleanup();
return 0;
}
/*************** PKEY API ***************/
static EVP_PKEY **pkey_new(lua_State *L)
{
EVP_PKEY **pkey = (EVP_PKEY **)lua_newuserdata(L, sizeof(EVP_PKEY *));
luaL_getmetatable(L, LUACRYPTO_PKEYNAME);
lua_setmetatable(L, -2);
return pkey;
}
#ifndef DONT_USE_OPENSSL_DEPRECATED_FUNCTIONS
static int pkey_generate(lua_State *L)
{
const char *options[] = {"rsa", "dsa", NULL};
int idx = luaL_checkoption(L, 1, NULL, options);
int key_len = luaL_checkinteger(L, 2);
EVP_PKEY **pkey = pkey_new(L);
if (idx == 0)
{
RSA *rsa = RSA_generate_key(key_len, RSA_F4, NULL, NULL);
if (!rsa)
return crypto_error(L);
*pkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(*pkey, rsa);
return 1;
}
else
{
DSA *dsa = DSA_generate_parameters(key_len, NULL, 0, NULL, NULL, NULL, NULL);
if (!DSA_generate_key(dsa))
return crypto_error(L);
*pkey = EVP_PKEY_new();
EVP_PKEY_assign_DSA(*pkey, dsa);
return 1;
}
}
#else
static int pkey_generate(lua_State *L)
{
const char *options[] = {"rsa", "dsa", NULL};
int idx = luaL_checkoption(L, 1, NULL, options);
int key_len = luaL_checkinteger(L, 2);
EVP_PKEY **pkey = pkey_new(L);
if (idx == 0)
{
BN_GENCB cb;
int i;
int success = 0;
int e_len = sizeof(RSA_F4) * 8;
RSA *rsa = RSA_new();
BIGNUM *e = BN_new();
if (rsa && e)
{
for (i = 0; i < e_len; ++i)
{
if (RSA_F4 & (1UL << i))
if (BN_set_bit(e, i) == 0)
break;
}
if (i == e_len)
{
BN_GENCB_set_old(&cb, NULL, NULL);
success = RSA_generate_key_ex(rsa, key_len, e, &cb);
}
}
if (e) BN_free(e);
if (!success || (*pkey = EVP_PKEY_new()) == NULL)
{
if (rsa) RSA_free(rsa);
return crypto_error(L);
}
EVP_PKEY_assign_RSA(*pkey, rsa);
return 1;
}
else
{
BN_GENCB cb;
int success = 0;
DSA *dsa = DSA_new();
if (dsa)
{
BN_GENCB_set_old(&cb, NULL, NULL);
if (DSA_generate_parameters_ex(dsa, key_len, NULL, 0, NULL, NULL, &cb))
success = DSA_generate_key(dsa);
}
if (!success || (*pkey = EVP_PKEY_new()) == NULL)
{
if (dsa) DSA_free(dsa);
return crypto_error(L);
}
EVP_PKEY_assign_DSA(*pkey, dsa);
return 1;
}
}
#endif
static int pkey_to_pem(lua_State *L)
{
EVP_PKEY **pkey = (EVP_PKEY **)luaL_checkudata(L, 1, LUACRYPTO_PKEYNAME);
int private = lua_isboolean(L, 2) && lua_toboolean(L, 2);
struct evp_pkey_st *pkey_st = *pkey;
int ret;
long len;
BUF_MEM *buf;
BIO *mem = BIO_new(BIO_s_mem());
if (private && pkey_st->type == EVP_PKEY_DSA)
ret = PEM_write_bio_DSAPrivateKey(mem, pkey_st->pkey.dsa, NULL, NULL, 0, NULL, NULL);
else if (private && pkey_st->type == EVP_PKEY_RSA)
ret = PEM_write_bio_RSAPrivateKey(mem, pkey_st->pkey.rsa, NULL, NULL, 0, NULL, NULL);
else if (private)
ret = PEM_write_bio_PrivateKey(mem, *pkey, NULL, NULL, 0, NULL, NULL);
else
ret = PEM_write_bio_PUBKEY(mem, *pkey);
if (ret == 0)
{
ret = crypto_error(L);
goto error;
}
len = BIO_get_mem_ptr(mem, &buf);
lua_pushlstring(L, buf->data, buf->length);
ret = 1;
error:
BIO_free(mem);
return ret;
}
static int pkey_read(lua_State *L)
{
const char *filename = luaL_checkstring(L, 1);
int readPrivate = lua_isboolean(L, 2) && lua_toboolean(L, 2);
FILE *fp = fopen(filename, "r");
EVP_PKEY **pkey = pkey_new(L);
if (!fp)
luaL_error(L, "File not found: %s", filename);
if (readPrivate)
*pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
else
*pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
fclose(fp);
if (! *pkey)
return crypto_error(L);
return 1;
}
static int pkey_from_pem(lua_State *L)
{
const char *key = luaL_checkstring(L, 1);
int private = lua_isboolean(L, 2) && lua_toboolean(L, 2);
EVP_PKEY **pkey = pkey_new(L);
BIO *mem = BIO_new(BIO_s_mem());
int ret;
ret = BIO_puts(mem, key);
if (ret != strlen(key))
{
goto error;
}
if (private)
*pkey = PEM_read_bio_PrivateKey(mem, NULL, NULL, NULL);
else
*pkey = PEM_read_bio_PUBKEY(mem, NULL, NULL, NULL);
if (! *pkey)
goto error;
return 1;
error:
BIO_free(mem);
return crypto_error(L);
}
static int pkey_write(lua_State *L)
{
EVP_PKEY **pkey = (EVP_PKEY **)luaL_checkudata(L, 1, LUACRYPTO_PKEYNAME);
const char *pubfn = lua_tostring(L, 2);
const char *privfn = lua_tostring(L, 3);
if (pubfn)
{
FILE *fp = fopen(pubfn, "w");
if (!fp)
luaL_error(L, "Unable to write to file: %s", pubfn);
if (!PEM_write_PUBKEY(fp, *pkey))
return crypto_error(L);
fclose(fp);
}
if (privfn)
{
FILE *fp = fopen(privfn, "w");
if (!fp)
luaL_error(L, "Unable to write to file: %s", privfn);
if (!PEM_write_PrivateKey(fp, *pkey, NULL, NULL, 0, NULL, NULL))
return crypto_error(L);
fclose(fp);
}
return 0;
}
static int pkey_gc(lua_State *L)
{
EVP_PKEY **pkey = (EVP_PKEY **)luaL_checkudata(L, 1, LUACRYPTO_PKEYNAME);
EVP_PKEY_free(*pkey);
return 0;
}
static int pkey_tostring(lua_State *L)
{
EVP_PKEY **pkey = (EVP_PKEY **)luaL_checkudata(L, 1, LUACRYPTO_PKEYNAME);
char buf[60];
sprintf(buf, "%s %s %d %p", LUACRYPTO_PKEYNAME, (*pkey)->type == EVP_PKEY_DSA ? "DSA" : "RSA", EVP_PKEY_bits(*pkey), pkey);
lua_pushstring(L, buf);
return 1;
}
/*************** SEAL API ***************/
typedef struct seal_context
{
EVP_CIPHER_CTX *ctx;
int eklen;
unsigned char iv[EVP_MAX_IV_LENGTH];
unsigned char *ek;
} seal_context;
static seal_context *seal_pnew(lua_State *L)
{
seal_context *c = (seal_context *)lua_newuserdata(L, sizeof(seal_context));
luaL_getmetatable(L, LUACRYPTO_SEALNAME);
lua_setmetatable(L, -2);
memset(c, 0, sizeof(seal_context));
c->ctx = (EVP_CIPHER_CTX *)malloc(sizeof(EVP_CIPHER_CTX));
return c;
}
static int seal_gc(lua_State *L)
{
seal_context *c = (seal_context *)luaL_checkudata(L, 1, LUACRYPTO_SEALNAME);
EVP_CIPHER_CTX_cleanup(c->ctx);
free(c->ctx);
if (c->ek != NULL)
{
free(c->ek);
}
return 0;
}
static int seal_tostring(lua_State *L)
{
seal_context *c = (seal_context *)luaL_checkudata(L, 1, LUACRYPTO_SEALNAME);
char s[64];
sprintf(s, "%s %p %s", LUACRYPTO_SEALNAME, (void *)c, EVP_CIPHER_name(c->ctx->cipher));
lua_pushstring(L, s);
return 1;
}
static int seal_fnew(lua_State *L)
{
const char *cipher_type = luaL_checkstring(L, 1);
const EVP_CIPHER *cipher = EVP_get_cipherbyname(cipher_type);
int npubk = 1;
EVP_PKEY **pkey;
seal_context *seal_ctx;
if (cipher == NULL)
return luaL_argerror(L, 1, "invalid encrypt cipher");
pkey = (EVP_PKEY **)luaL_checkudata(L, 2, LUACRYPTO_PKEYNAME);
seal_ctx = seal_pnew(L);
EVP_CIPHER_CTX_init(seal_ctx->ctx);
seal_ctx->ek = (unsigned char *)malloc((size_t)EVP_PKEY_size(*pkey) * (size_t)npubk);
if (!EVP_SealInit(seal_ctx->ctx, cipher, &seal_ctx->ek, &seal_ctx->eklen,
seal_ctx->iv, pkey, npubk))
{
free(seal_ctx->ek);
seal_ctx->ek = NULL;
return crypto_error(L);
}
return 1;
}
static int seal_update(lua_State *L)
{
seal_context *c = (seal_context *)luaL_checkudata(L, 1, LUACRYPTO_SEALNAME);
size_t input_len = 0;
const unsigned char *input = (unsigned char *)luaL_checklstring(L, 2, &input_len);
int output_len = 0;
unsigned char *temp = (unsigned char *)malloc(input_len + (size_t)EVP_CIPHER_CTX_block_size(c->ctx));
EVP_SealUpdate(c->ctx, temp, &output_len, input, (int)input_len);
lua_pushlstring(L, (char *)temp, (size_t)output_len);
free(temp);
return 1;
}
static int seal_final(lua_State *L)
{
seal_context *c = (seal_context *)luaL_checkudata(L, 1, LUACRYPTO_SEALNAME);
int output_len = 0;
unsigned char buffer[EVP_MAX_BLOCK_LENGTH];
EVP_SealFinal(c->ctx, buffer, &output_len);
lua_pushlstring(L, (char *)buffer, (size_t)output_len);
lua_pushlstring(L, (const char *)c->ek, (size_t)c->eklen);
lua_pushlstring(L, (const char *)c->iv, (size_t)EVP_CIPHER_iv_length(c->ctx->cipher));
free(c->ek);
c->ek = NULL;
return 3;
}
static int seal_fseal(lua_State *L)
{
/* parameter 1 is the 'crypto.seal' table */
const char *cipher_type = luaL_checkstring(L, 2);
const EVP_CIPHER *cipher = EVP_get_cipherbyname(cipher_type);
EVP_PKEY **pkey = (EVP_PKEY **)luaL_checkudata(L, 4, LUACRYPTO_PKEYNAME);
int npubk = 1;
int eklen;
unsigned char iv[EVP_MAX_IV_LENGTH];
const unsigned char *message;
int message_length;
unsigned char *ek;
int block_size;
EVP_CIPHER_CTX ctx;
luaL_Buffer buffer;
int output_length;
char *temp;
int sz;
if (cipher == NULL)
{
luaL_argerror(L, 1, "invalid encrypt cipher");
return 0;
}
message = (const unsigned char *)luaL_checkstring(L, 3);
message_length = (int)lua_objlen(L, 3);
ek = (unsigned char *)malloc((size_t)EVP_PKEY_size(*pkey) * (size_t)npubk);
EVP_CIPHER_CTX_init(&ctx);
if (!EVP_SealInit(&ctx, cipher, &ek, &eklen, iv, pkey, npubk))
{
free(ek);
EVP_CIPHER_CTX_cleanup(&ctx);
return crypto_error(L);
}
luaL_buffinit(L, &buffer);
block_size = EVP_CIPHER_block_size(cipher);
while (message_length > 0)
{
temp = luaL_prepbuffer(&buffer);
sz = MIN(LUAL_BUFFERSIZE - block_size - 1, message_length);
if (!EVP_SealUpdate(&ctx, (unsigned char *)temp, &output_length, message, sz))
{
free(ek);
EVP_CIPHER_CTX_cleanup(&ctx);
return crypto_error(L);
}
message += sz;
message_length -= sz;
luaL_addsize(&buffer, output_length);
}
temp = luaL_prepbuffer(&buffer);
if (!EVP_SealFinal(&ctx, (unsigned char *)temp, &output_length))
{
free(ek);
EVP_CIPHER_CTX_cleanup(&ctx);
return crypto_error(L);
}
luaL_addsize(&buffer, output_length);
luaL_pushresult(&buffer);
lua_pushlstring(L, (const char *)ek, (size_t)eklen);
lua_pushlstring(L, (const char *)iv, (size_t)EVP_CIPHER_iv_length(cipher));
EVP_CIPHER_CTX_cleanup(&ctx);
free(ek);
return 3;
}
/*************** OPEN API ***************/
typedef struct open_context
{
EVP_CIPHER_CTX *ctx;
EVP_CIPHER *cipher;
int pkey_ref;
} open_context;
static open_context *open_pnew(lua_State *L)
{
open_context *c = (open_context *)lua_newuserdata(L, sizeof(open_context));
luaL_getmetatable(L, LUACRYPTO_OPENNAME);
lua_setmetatable(L, -2);
memset(c, 0, sizeof(open_context));
c->ctx = (EVP_CIPHER_CTX *)malloc(sizeof(EVP_CIPHER_CTX));
c->pkey_ref = LUA_NOREF;
return c;
}
static int open_gc(lua_State *L)
{
open_context *c = luaL_checkudata(L, 1, LUACRYPTO_OPENNAME);
EVP_CIPHER_CTX_cleanup(c->ctx);
free(c->ctx);
if (c->pkey_ref != LUA_NOREF)
{
luaL_unref(L, LUA_REGISTRYINDEX, c->pkey_ref);
}
return 0;
}
static int open_tostring(lua_State *L)
{
open_context *c = (open_context *)luaL_checkudata(L, 1, LUACRYPTO_OPENNAME);
EVP_PKEY **pkey = (EVP_PKEY **)luaL_checkudata(L, -1, LUACRYPTO_PKEYNAME);
char s[64];
lua_rawgeti(L, LUA_REGISTRYINDEX, c->pkey_ref);
sprintf(s, "%s %p %s %s %d %p", LUACRYPTO_OPENNAME, (void *)c, EVP_CIPHER_name(c->cipher),
(*pkey)->type == EVP_PKEY_DSA ? "DSA" : "RSA", EVP_PKEY_bits(*pkey), pkey);
lua_pop(L, 1);
lua_pushstring(L, s);
return 1;
}
static int open_fnew(lua_State *L)
{
const char *type_name = luaL_checkstring(L, 1);
const EVP_CIPHER *cipher = EVP_get_cipherbyname(type_name);
const unsigned char *encrypted_key;
const unsigned char *iv;
EVP_PKEY **pkey;
open_context *open_ctx;
if (cipher == NULL)
return luaL_argerror(L, 1, "invalid decrypt cipher");
pkey = (EVP_PKEY **)luaL_checkudata(L, 2, LUACRYPTO_PKEYNAME);
/* checks for the encrypted key */
encrypted_key = (const unsigned char *)luaL_checkstring(L, 3);
/* checks for the initialization vector */
iv = (const unsigned char *)luaL_checkstring(L, 4);
if ((size_t)EVP_CIPHER_iv_length(cipher) != lua_objlen(L, 4))
return luaL_argerror(L, 4, "invalid iv length");
open_ctx = open_pnew(L);
EVP_CIPHER_CTX_init(open_ctx->ctx);
open_ctx->cipher = (EVP_CIPHER *)cipher;
if (!EVP_OpenInit(open_ctx->ctx, open_ctx->cipher, encrypted_key,
(int)lua_objlen(L, 3), iv, *pkey))
return crypto_error(L);
lua_pushvalue(L, 2);
open_ctx->pkey_ref = luaL_ref(L, LUA_REGISTRYINDEX);
return 1;
}
static int open_update(lua_State *L)
{
open_context *c = (open_context *)luaL_checkudata(L, 1, LUACRYPTO_OPENNAME);
size_t input_len = 0;
const unsigned char *input = (unsigned char *)luaL_checklstring(L, 2, &input_len);
luaL_Buffer buffer;
luaL_buffinit(L, &buffer);
while (input_len > 0)
{
int output_length;
unsigned char *temp = (unsigned char *)luaL_prepbuffer(&buffer);
size_t sz = MIN(LUAL_BUFFERSIZE - 1, input_len);
if (!EVP_OpenUpdate(c->ctx, temp, &output_length, input, (int)sz))
{
return crypto_error(L);
}
input += sz;
input_len -= sz;
luaL_addsize(&buffer, output_length);
}
luaL_pushresult(&buffer);
return 1;
}
static int open_final(lua_State *L)
{
open_context *c = (open_context *)luaL_checkudata(L, 1, LUACRYPTO_OPENNAME);
int output_len = 0;
unsigned char buffer[EVP_MAX_BLOCK_LENGTH];
EVP_OpenFinal(c->ctx, buffer, &output_len);
lua_pushlstring(L, (char *)buffer, (size_t)output_len);
return 1;
}
static int open_fopen(lua_State *L)
{
/* parameter 1 is the 'crypto.open' table */
const char *type_name = luaL_checkstring(L, 2);
const EVP_CIPHER *cipher = EVP_get_cipherbyname(type_name);
unsigned char *data;
int data_length;
EVP_PKEY **pkey;
const unsigned char *encrypted_key;
const unsigned char *iv;
EVP_CIPHER_CTX ctx;
int eklen;
int output_length;
unsigned char *temp;
size_t sz;
luaL_Buffer buffer;
if (cipher == NULL)
{
luaL_argerror(L, 1, "invalid decrypt cipher");
return 0;
}
data = (unsigned char *)luaL_checkstring(L, 3);
data_length = (int)lua_objlen(L, 3);
pkey = (EVP_PKEY **)luaL_checkudata(L, 4, LUACRYPTO_PKEYNAME);
encrypted_key = (const unsigned char *)luaL_checkstring(L, 5);
iv = (const unsigned char *)luaL_checkstring(L, 6);
if ((size_t)EVP_CIPHER_iv_length(cipher) != lua_objlen(L, 6))
{
luaL_argerror(L, 6, "invalid iv length");
return 0;
}
EVP_CIPHER_CTX_init(&ctx);
eklen = (int)lua_objlen(L, 5);
if (!EVP_OpenInit(&ctx, cipher, encrypted_key, eklen, iv, *pkey))
{
EVP_CIPHER_CTX_cleanup(&ctx);
return crypto_error(L);
}
luaL_buffinit(L, &buffer);
while (data_length > 0)
{
temp = (unsigned char *)luaL_prepbuffer(&buffer);
sz = MIN(LUAL_BUFFERSIZE - 1U, (size_t)data_length);
if (!EVP_OpenUpdate(&ctx, temp, &output_length, data, (int)sz))
{
EVP_CIPHER_CTX_cleanup(&ctx);
return crypto_error(L);
}
data += sz;
data_length -= (int)sz;
luaL_addsize(&buffer, output_length);
}
temp = (unsigned char *)luaL_prepbuffer(&buffer);
if (!EVP_OpenFinal(&ctx, temp, &output_length))
{
EVP_CIPHER_CTX_cleanup(&ctx);
return crypto_error(L);
}
luaL_addsize(&buffer, output_length);
luaL_pushresult(&buffer);
EVP_CIPHER_CTX_cleanup(&ctx);
return 1;
}
/*************** CORE API ***************/
static void list_callback(const OBJ_NAME *obj, void *arg)
{
lua_State *L = (lua_State *)arg;
int idx = (int)lua_objlen(L, -1);
lua_pushstring(L, obj->name);
lua_rawseti(L, -2, idx + 1);
}
static int luacrypto_list(lua_State *L)
{
int options[] = {OBJ_NAME_TYPE_CIPHER_METH, OBJ_NAME_TYPE_MD_METH};
const char *names[] = {"ciphers", "digests", NULL};
int idx = luaL_checkoption (L, 1, NULL, names);
lua_createtable(L, 0, 0);
OBJ_NAME_do_all_sorted(options[idx], list_callback, L);
return 1;
}
static int luacrypto_hex(lua_State *L)
{
size_t i, len = 0;
const unsigned char *input = (unsigned char *)luaL_checklstring(L, 1, &len);
char *hex = (char *)calloc(sizeof(char), len * 2 + 1);
for (i = 0; i < len; i++)
{
sprintf(hex + 2 * i, "%02x", input[i]);
}
lua_pushlstring(L, hex, len * 2);
free(hex);
return 1;
}
/*************** x509 API ***************/
static X509 *x509__load_cert(BIO *cert)
{
X509 *x = PEM_read_bio_X509_AUX(cert, NULL, NULL, NULL);
return x;
}
static X509 *x509__x509_from_string(const char *pem)
{
BIO *mem = BIO_new(BIO_s_mem());
X509 *cert;
int ret;
if (!mem)
return NULL;
ret = BIO_puts(mem, pem);
if (ret != (int)strlen(pem))
goto error;
cert = x509__load_cert(mem);
if (cert == NULL)
goto error;
return cert;
error:
BIO_free(mem);
return NULL;
}
struct x509_cert
{
X509 *cert;
};
static struct x509_cert *x509_cert__get(lua_State *L)
{
return luaL_checkudata(L, 1, LUACRYPTO_X509_CERT_NAME);
}
static int x509_cert_fnew(lua_State *L)
{
struct x509_cert *x = lua_newuserdata(L, sizeof(struct x509_cert));
x->cert = NULL;
luaL_getmetatable(L, LUACRYPTO_X509_CERT_NAME);
lua_setmetatable(L, -2);
return 1;
}
static int x509_cert_fx509_cert(lua_State *L)
{
return x509_cert_fnew(L);
}
static int x509_cert_gc(lua_State *L)
{
struct x509_cert *c = x509_cert__get(L);
X509_free(c->cert);
return 0;
}
static int x509_cert_from_pem(lua_State *L)
{
struct x509_cert *x = x509_cert__get(L);
const char *pem = luaL_checkstring(L, 2);
if (x->cert != NULL)
X509_free(x->cert);
x->cert = x509__x509_from_string(pem);
if (!x->cert)
return crypto_error(L);
return 1;
}
static int x509_cert_pubkey(lua_State *L)
{
struct x509_cert *x = x509_cert__get(L);
EVP_PKEY **out_pkey;
EVP_PKEY *pkey = X509_get_pubkey(x->cert);
if (!pkey)
return crypto_error(L);
out_pkey = pkey_new(L);
*out_pkey = pkey;
return 1;
}
struct x509_ca
{
X509_STORE *store;
STACK_OF(X509) *stack;
};
static int x509_ca_fnew(lua_State *L)
{
struct x509_ca *x = lua_newuserdata(L, sizeof(struct x509_ca));
x->store = X509_STORE_new();
x->stack = sk_X509_new_null();
luaL_getmetatable(L, LUACRYPTO_X509_CA_NAME);
lua_setmetatable(L, -2);
return 1;
}
static int x509_ca_fx509_ca(lua_State *L)
{
return x509_ca_fnew(L);
}
static struct x509_ca *x509_ca__get(lua_State *L)
{
return luaL_checkudata(L, 1, LUACRYPTO_X509_CA_NAME);
}
static int x509_ca_gc(lua_State *L)
{
struct x509_ca *c = x509_ca__get(L);
sk_X509_pop_free(c->stack, X509_free);
X509_STORE_free(c->store);
return 0;
}
/* verify a cert is signed by the ca */
static int x509_ca__verify(struct x509_ca *x, X509 *cert)
{
X509_STORE_CTX *csc;
int ret = -1;
csc = X509_STORE_CTX_new();
if (csc == NULL)
return ret;
X509_STORE_set_flags(x->store, 0);
if (!X509_STORE_CTX_init(csc, x->store, cert, 0))
goto out;
X509_STORE_CTX_trusted_stack(csc, x->stack);
ret = X509_verify_cert(csc);
out:
X509_STORE_CTX_free(csc);
return ret;
}
/* verify a cert is signed by the ca */
static int x509_ca_verify_pem(lua_State *L)
{
struct x509_ca *x = x509_ca__get(L);
const char *pem = luaL_checkstring(L, 2);
X509 *cert;
int ret;
cert = x509__x509_from_string(pem);
if (!cert)
return crypto_error(L);
ret = x509_ca__verify(x, cert);
X509_free(cert);
if (ret < 0)
return crypto_error(L);
lua_pushboolean(L, ret);
return 1;
}
static int x509_ca_add_pem(lua_State *L)
{
struct x509_ca *x = x509_ca__get(L);
const char *pem = luaL_checkstring(L, 2);
X509 *cert;
cert = x509__x509_from_string(pem);
if (!cert)
return crypto_error(L);
sk_X509_push(x->stack, cert);
lua_pushboolean(L, 1);
return 1;
}
/*
** Create a metatable and leave it on top of the stack.
*/
LUACRYPTO_API int luacrypto_createmeta (lua_State *L, const char *name, const luaL_Reg *methods)
{
if (!luaL_newmetatable (L, name))
return 0;
/* define methods */
luaL_register (L, NULL, methods);
/* define metamethods */
lua_pushliteral (L, "__index");
lua_pushvalue (L, -2);
lua_settable (L, -3);
lua_pushliteral (L, "__metatable");
lua_pushliteral (L, LUACRYPTO_PREFIX"you're not allowed to get this metatable");
lua_settable (L, -3);
return 1;
}
static void create_call_table(lua_State *L, const char *name, lua_CFunction creator, lua_CFunction starter)
{
lua_createtable(L, 0, 1);
lua_pushcfunction(L, creator);
lua_setfield(L, -2, "new");
/* create metatable for call */
lua_createtable(L, 0, 1);
lua_pushcfunction(L, starter);
lua_setfield(L, -2, "__call");
lua_setmetatable(L, -2);
lua_setfield(L, -2, name);
}
#define EVP_METHODS(name) \
struct luaL_Reg name##_methods[] = { \
{ "__tostring", name##_tostring }, \
{ "__gc", name##_gc }, \
{ "final", name##_final }, \
{ "tostring", name##_tostring }, \
{ "update", name##_update }, \
{NULL, NULL}, \
}
/*
** Create metatables for each class of object.
*/
static void create_metatables (lua_State *L)
{
int top;
struct luaL_Reg core_functions[] =
{
{ "list", luacrypto_list },
{ "hex", luacrypto_hex },
{ NULL, NULL }
};
struct luaL_Reg digest_methods[] =
{
{ "__tostring", digest_tostring },
{ "__gc", digest_gc },
{ "final", digest_final },
{ "tostring", digest_tostring },
{ "update", digest_update },
{ "reset", digest_reset },
{ "clone", digest_clone },
{NULL, NULL}
};
EVP_METHODS(encrypt);
EVP_METHODS(decrypt);
EVP_METHODS(sign);
EVP_METHODS(verify);
EVP_METHODS(seal);
EVP_METHODS(open);
struct luaL_Reg hmac_functions[] =
{
{ "digest", hmac_fdigest },
{ "new", hmac_fnew },
{ NULL, NULL }
};
struct luaL_Reg hmac_methods[] =
{
{ "__tostring", hmac_tostring },
{ "__gc", hmac_gc },
{ "clone", hmac_clone },
{ "final", hmac_final },
{ "reset", hmac_reset },
{ "tostring", hmac_tostring },
{ "update", hmac_update },
{ NULL, NULL }
};
struct luaL_Reg rand_functions[] =
{
{ "bytes", rand_bytes },
{ "pseudo_bytes", rand_pseudo_bytes },
{ "add", rand_add },
{ "seed", rand_add },
{ "status", rand_status },
{ "load", rand_load },
{ "write", rand_write },
{ "cleanup", rand_cleanup },
{ NULL, NULL }
};
struct luaL_Reg pkey_functions[] =
{
{ "generate", pkey_generate },
{ "read", pkey_read },
{ "from_pem", pkey_from_pem },
{ NULL, NULL }
};
struct luaL_Reg pkey_methods[] =
{
{ "__tostring", pkey_tostring },
{ "__gc", pkey_gc },
{ "write", pkey_write },
{ "to_pem", pkey_to_pem},
{ NULL, NULL }
};
struct luaL_Reg x509_functions[] =
{
{ NULL, NULL }
};
struct luaL_Reg x509_methods[] =
{
{ "__gc", x509_cert_gc },
{ "from_pem", x509_cert_from_pem},
{ "pubkey", x509_cert_pubkey},
{ NULL, NULL }
};
struct luaL_Reg x509_ca_functions[] =
{
{ NULL, NULL }
};
struct luaL_Reg x509_ca_methods[] =
{
{ "__gc", x509_ca_gc },
{ "add_pem", x509_ca_add_pem },
{ "verify_pem", x509_ca_verify_pem },
{ NULL, NULL }
};
luaL_register (L, LUACRYPTO_CORENAME, core_functions);
top = lua_gettop(L);
#define CALLTABLE(n) create_call_table(L, #n, n##_fnew, n##_f##n)
CALLTABLE(digest);
CALLTABLE(encrypt);
CALLTABLE(decrypt);
CALLTABLE(verify);
CALLTABLE(sign);
CALLTABLE(seal);
CALLTABLE(open);
CALLTABLE(x509_cert);
CALLTABLE(x509_ca);
assert(top == lua_gettop(L));
luacrypto_createmeta(L, LUACRYPTO_DIGESTNAME, digest_methods);
luacrypto_createmeta(L, LUACRYPTO_ENCRYPTNAME, encrypt_methods);
luacrypto_createmeta(L, LUACRYPTO_DECRYPTNAME, decrypt_methods);
luacrypto_createmeta(L, LUACRYPTO_HMACNAME, hmac_methods);
luacrypto_createmeta(L, LUACRYPTO_SIGNNAME, sign_methods);
luacrypto_createmeta(L, LUACRYPTO_VERIFYNAME, verify_methods);
luacrypto_createmeta(L, LUACRYPTO_PKEYNAME, pkey_methods);
luacrypto_createmeta(L, LUACRYPTO_SEALNAME, seal_methods);
luacrypto_createmeta(L, LUACRYPTO_OPENNAME, open_methods);
luacrypto_createmeta(L, LUACRYPTO_X509_CERT_NAME, x509_methods);
luacrypto_createmeta(L, LUACRYPTO_X509_CA_NAME, x509_ca_methods);
lua_settop(L, top);
luacrypto_register_submodule (L, LUACRYPTO_RANDNAME, rand_functions);
luacrypto_register_submodule (L, LUACRYPTO_HMACNAME, hmac_functions);
luacrypto_register_submodule (L, LUACRYPTO_PKEYNAME, pkey_functions);
luacrypto_register_submodule (L, LUACRYPTO_X509_CERT_NAME, x509_functions);
luacrypto_register_submodule (L, LUACRYPTO_X509_CA_NAME, x509_ca_functions);
assert(top == lua_gettop(L));
}
/*
** Define the metatable for the object on top of the stack
*/
LUACRYPTO_API void luacrypto_setmeta (lua_State *L, const char *name)
{
luaL_getmetatable (L, name);
lua_setmetatable (L, -2);
}
/*
** Assumes the table is on top of the stack.
*/
LUACRYPTO_API void luacrypto_set_info (lua_State *L)
{
lua_pushliteral (L, "_COPYRIGHT");
lua_pushliteral (L, "Copyright (C) 2005-2006 Keith Howe");
lua_settable (L, -3);
lua_pushliteral (L, "_DESCRIPTION");
lua_pushliteral (L, "LuaCrypto is a Lua wrapper for OpenSSL");
lua_settable (L, -3);
lua_pushliteral (L, "_VERSION");
lua_pushliteral (L, "LuaCrypto 0.3.1");
lua_settable (L, -3);
}
/*
** Creates the metatables for the objects and registers the
** driver open method.
*/
LUACRYPTO_API int luaopen_crypto(lua_State *L)
{
struct luaL_Reg core[] =
{
{NULL, NULL},
};
#ifndef OPENSSL_EXTERNAL_INITIALIZATION
OpenSSL_add_all_digests();
OpenSSL_add_all_ciphers();
#endif
create_metatables (L);
luaL_register (L, NULL, core);
luacrypto_set_info (L);
return 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment