text: /* Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements. 
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   * --------------------------------------------------------------------------- 
   * This work is derived from the famos luacrypto library:
   *
   * The LuaCrypto library is designed and implemented by Keith Howe. The 
   * implementation is not derived from licensed software.
   *
   * Copyright © 2006 Keith Howe.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to 
   * deal in the Software without restriction, including without limitation the 
   * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 
   * sell copies of the Software, and to permit persons to whom the Software is 
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in all 
   * copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
   * IN THE SOFTWARE.
   * ---------------------------------------------------------------------------
   */
  
  /**
   * @file
   *
   * @Author christian liesch <liesch@gmx.ch>
   *
   * Interface of the HTTP Test Tool lua crypto extention.
   */
  
  
  /************************************************************************
   * Includes
   ***********************************************************************/
  
  #ifdef HAVE_CONFIG_H
  #include <config.h>
  #endif
  
  #include <string.h>
  #include <openssl/ssl.h>
  #include <openssl/err.h>
  #include <openssl/asn1.h>
  #include <openssl/evp.h>
  #include <openssl/hmac.h>
  #include <openssl/rand.h>
  #include <openssl/x509.h>
  #include <openssl/dh.h>
  
  #define LUA_COMPAT_MODULE
  #include "lua.h"
  #include "lauxlib.h"
  #if ! defined (LUA_VERSION_NUM) || LUA_VERSION_NUM < 501
  #include "compat-5.1.h"
  #endif
  
  #include "lua_crypto.h"
  #include "module.h"
  
  /************************************************************************
   * Definitions 
   ***********************************************************************/
  
  #define LUACRYPTO_PREFIX "LuaCrypto: "
  #define LUACRYPTO_CORE "crypto"
  #define LUACRYPTO_EVP "crypto.evp"
  #define LUACRYPTO_HMAC "crypto.hmac"
  #define LUACRYPTO_RAND "crypto.rand"
  #define LUACRYPTO_BASE64 "crypto.base64"
  #define LUACRYPTO_X509 "crypto.x509"
  #define LUACRYPTO_X509NAME "crypto.x509name"
  #define LUACRYPTO_ASN1TIME "crypto.asn1time"
  #define LUACRYPTO_DH "crypto.dh"
  
  
  /************************************************************************
   * Forward declaration 
   ***********************************************************************/
  
  int luaopen_crypto(lua_State *L);
  
  
  /************************************************************************
   * Implementation 
   ***********************************************************************/
  
  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));
    return 2;
  }
  
  /**
   * EVP Object
   */
  static EVP_MD_CTX *evp_pget(lua_State *L, int i) {
    if (luaL_checkudata(L, i, LUACRYPTO_EVP) == NULL) {
      luaL_argerror(L, 1, "invalid object type");
    }
    return lua_touserdata(L, i);
  }
  
  static EVP_MD_CTX *evp_pnew(lua_State *L) {
    EVP_MD_CTX *c = EVP_MD_CTX_new();
    lua_pushlightuserdata(L, c);
    luaL_getmetatable(L, LUACRYPTO_EVP);
    lua_setmetatable(L, -2);
    return c;
  }
  
  static int evp_fnew(lua_State *L) {
    EVP_MD_CTX *c = NULL;
    const char *s = luaL_checkstring(L, 1);
    const EVP_MD *type = EVP_get_digestbyname(s);
    
    if (type == NULL) {
      luaL_argerror(L, 1, "invalid digest type");
      return 0;
    }
    
    c = evp_pnew(L);
    EVP_MD_CTX_init(c);
    EVP_DigestInit_ex(c, type, NULL);
    
    return 1;
  }
  
  static int evp_clone(lua_State *L) {
    EVP_MD_CTX *c = evp_pget(L, 1);
    EVP_MD_CTX *d = evp_pnew(L);
    EVP_MD_CTX_init(d);
    EVP_MD_CTX_copy_ex(d, c);
    return 1;
  }
  
  static int evp_reset(lua_State *L) {
    EVP_MD_CTX *c = evp_pget(L, 1);
    const EVP_MD *t = EVP_MD_CTX_md(c);
    EVP_MD_CTX_reset(c);
    EVP_MD_CTX_init(c);
    EVP_DigestInit_ex(c, t, NULL);
    return 0;
  }
  
  static int evp_update(lua_State *L) {
    EVP_MD_CTX *c = evp_pget(L, 1);
    const char *s = luaL_checkstring(L, 2);
    
    EVP_DigestUpdate(c, s, strlen(s));
    
    lua_settop(L, 1);
    return 1;
  }
  
  static int evp_digest(lua_State *L) {
    EVP_MD_CTX *c = evp_pget(L, 1);
    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)) {  
      const char *s = luaL_checkstring(L, 2);
      EVP_DigestUpdate(c, s, strlen(s));
    }
    
    d = EVP_MD_CTX_create();
    EVP_MD_CTX_copy_ex(d, c);
    EVP_DigestFinal_ex(d, digest, &written);
    EVP_MD_CTX_free(d);
    
    if (lua_toboolean(L, 3)) {
      lua_pushlstring(L, (char *)digest, written);
    }
    else {
      hex = 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 evp_tostring(lua_State *L) {
    EVP_MD_CTX *c = evp_pget(L, 1);
    char s[64];
    sprintf(s, "%s %p", LUACRYPTO_EVP, (void *)c);
    lua_pushstring(L, s);
    return 1;
  }
  
  static int evp_gc(lua_State *L) {
    EVP_MD_CTX *c = evp_pget(L, 1);
    EVP_MD_CTX_free(c);
    return 1;
  }
  
  static int evp_fdigest(lua_State *L) {
    EVP_MD_CTX *c = NULL;
    const char *type_name = luaL_checkstring(L, 1);
    const char *s = luaL_checkstring(L, 2);
    const EVP_MD *type = EVP_get_digestbyname(type_name);
    unsigned char digest[EVP_MAX_MD_SIZE];
    unsigned int written = 0;
    unsigned int i;
    char *hex;
    
    if (type == NULL) {
      luaL_argerror(L, 1, "invalid digest type");
      return 0;
    }
    
    c = EVP_MD_CTX_create();
    EVP_DigestInit_ex(c, type, NULL);
    EVP_DigestUpdate(c, s, strlen(s));
    EVP_DigestFinal_ex(c, digest, &written);
    
    if (lua_toboolean(L, 3)) {
      lua_pushlstring(L, (char *)digest, written);
    }
    else {
      hex = 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;
  }
  
  /**
   * HMAC Object
   */
  static HMAC_CTX *hmac_pget(lua_State *L, int i) {
   if (luaL_checkudata(L, i, LUACRYPTO_HMAC) == NULL) {
      luaL_argerror(L, 1, "invalid object type");
   }
   return lua_touserdata(L, i);
  }
  
  static HMAC_CTX *hmac_pnew(lua_State *L) {
    EVP_MD_CTX *c = HMAC_CTX_new();
    lua_pushlightuserdata(L, c);
    luaL_getmetatable(L, LUACRYPTO_HMAC);
    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);
    const char *k = luaL_checkstring(L, 2);
    const EVP_MD *type = EVP_get_digestbyname(s);
  
    if (type == NULL) {
      luaL_argerror(L, 1, "invalid digest type");
      return 0;
    }
  
    HMAC_CTX_reset(c);
    HMAC_Init_ex(c, k, strlen(k), type, NULL);
  
    return 1;
  }
  
  static int hmac_clone(lua_State *L) {
   HMAC_CTX *c = hmac_pget(L, 1);
   HMAC_CTX *d = hmac_pnew(L);
   HMAC_CTX_copy(d, c);
   return 1;
  }
  
  static int hmac_reset(lua_State *L) {
    HMAC_CTX *c = hmac_pget(L, 1);
    HMAC_Init_ex(c, NULL, 0, NULL, NULL);
    return 0;
  }
  
  static int hmac_update(lua_State *L) {
    HMAC_CTX *c = hmac_pget(L, 1);
    const char *s = luaL_checkstring(L, 2);
  
    HMAC_Update(c, (unsigned char *)s, strlen(s));
  
    lua_settop(L, 1);
    return 1;
  }
  
  static int hmac_digest(lua_State *L) {
    HMAC_CTX *c = hmac_pget(L, 1);
    unsigned char digest[EVP_MAX_MD_SIZE];
    unsigned int written = 0;
    unsigned int i;
    char *hex;
  
    if (lua_isstring(L, 2))
    {
      const char *s = luaL_checkstring(L, 2);
      HMAC_Update(c, (unsigned char *)s, strlen(s));
    }
  
    HMAC_Final(c, digest, &written);
  
    if (lua_toboolean(L, 3)) {
      lua_pushlstring(L, (char *)digest, written);
    }
    else {
      hex = 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_pget(L, 1);
    char s[64];
    sprintf(s, "%s %p", LUACRYPTO_HMAC, (void *)c);
    lua_pushstring(L, s);
    return 1;
  }
  
  static int hmac_gc(lua_State *L) {
    HMAC_CTX *c = hmac_pget(L, 1);
    HMAC_CTX_free(c);
    return 1;
  }
  
  static int hmac_fdigest(lua_State *L) {
    HMAC_CTX *c = hmac_pnew(L);
    unsigned char digest[EVP_MAX_MD_SIZE];
    unsigned int written = 0;
    unsigned int i;
    char *hex;
    const char *t = luaL_checkstring(L, 1);
    const char *s = luaL_checkstring(L, 2);
    const char *k = luaL_checkstring(L, 3);
    const EVP_MD *type = EVP_get_digestbyname(t);
  
    if (type == NULL) {
      luaL_argerror(L, 1, "invalid digest type");
      return 0;
    }
  
    HMAC_CTX_reset(c);
    HMAC_Init_ex(c, k, strlen(k), type, NULL);
    HMAC_Update(c, (unsigned char *)s, strlen(s));
    HMAC_Final(c, digest, &written);
  
    if (lua_toboolean(L, 4)) {
      lua_pushlstring(L, (char *)digest, written);
    }
    else {
      hex = 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;
  }
  
  /**
   * Random Object
   */
  static int rand_do_bytes(lua_State *L, int (*bytes)(unsigned char *buf, int len)) {
    size_t count = luaL_checkint(L, 1);
    unsigned char tmp[256], *buf = tmp;
    if (count > sizeof tmp) {
      buf = malloc(count);
    }
    if (!buf) {
      return luaL_error(L, "out of memory");
    }
    else if (!bytes(buf, 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, 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) {
    RAND_cleanup();
    return 0;
  }
  
  /**
   * Base64
   */
  static int b64_encode(lua_State *L) {
    if (lua_isstring(L, -1)) {
      apr_pool_t *pool;
      apr_size_t len;
      apr_size_t b64len;
      char *base64;
  
      const char *buffer = lua_tolstring(L, -1, &len);
  
      HT_POOL_CREATE(&pool);
      b64len = apr_base64_encode_len(len);
      base64 = apr_pcalloc(pool, b64len + 1);
      apr_base64_encode(base64, buffer, len);
  
      lua_pushstring(L, base64);
      apr_pool_destroy(pool);
      return 1;
    }
    else {
      luaL_error(L, "Expect a string parameter");
      return 1;
    }
  }
  
  static int b64_decode(lua_State *L) {
    if (lua_isstring(L, -1)) {
      apr_pool_t *pool;
      apr_size_t len;
      unsigned char *plain;
  
      const char *buffer = lua_tolstring(L, -1, &len);
  
      HT_POOL_CREATE(&pool);
  
      len = apr_base64_decode_len(buffer);
      plain = apr_pcalloc(pool, len);
      apr_base64_decode_binary(plain, buffer);
  
      lua_pushlstring(L, (char *)plain, len);
      apr_pool_destroy(pool);
      return 1;
    }
    else {
      luaL_error(L, "Expect a string parameter");
      return 1;
    }
  }
  
  /**
   * X509 Object
   */
  static X509 *x509_pget(lua_State *L, int i) {
    if (luaL_checkudata(L, i, LUACRYPTO_X509) == NULL) {
      luaL_argerror(L, 1, "invalid object type");
    }
    return lua_touserdata(L, i);
  }
  
  static int x509_fnew(lua_State *L) {
    apr_size_t len;
    const char *data = luaL_checklstring(L, 1, &len);
    X509 *cert;
    BIO *mem;
  
    if (data == NULL) {
      luaL_argerror(L, 1, "PEM cert to load");
      return 0;
    }
    
    mem = BIO_new_mem_buf((void *)data, len);
    cert = PEM_read_bio_X509(mem, NULL, NULL, NULL);
    
    lua_pushlightuserdata(L, cert);
    luaL_getmetatable(L, LUACRYPTO_X509);
    lua_setmetatable(L, -2);
  
    return 1;
  }
  
  static int x509_fload(lua_State *L) {
    const char *filename = luaL_checkstring(L, 1);
    X509 *cert;
    BIO *file;
  
    if (filename == NULL) {
      luaL_argerror(L, 1, "path to x509 pem formated cert missing");
      return 0;
    }
   
    file = BIO_new_file(filename, "r");
    cert = PEM_read_bio_X509(file, NULL, NULL, NULL);
    
    lua_pushlightuserdata(L, cert);
    luaL_getmetatable(L, LUACRYPTO_X509);
    lua_setmetatable(L, -2);
  
    return 1;
  }
  
  static int x509_clone(lua_State *L) {
    X509 *cert = x509_pget(L, 1);
    X509 *copy = X509_dup(cert);
  
    lua_pushlightuserdata(L, copy);
    luaL_getmetatable(L, LUACRYPTO_X509);
    lua_setmetatable(L, -2);
    return 1;
  }
  
  static int x509_get_subject_name(lua_State *L) {
    X509 *cert = x509_pget(L, 1);
    X509_NAME *name = X509_get_subject_name(cert);
  
    lua_pushlightuserdata(L, name);
    luaL_getmetatable(L, LUACRYPTO_X509NAME);
    lua_setmetatable(L, -2);
    return 1;
  }
  
  static int x509_get_issuer_name(lua_State *L) {
    X509 *cert = x509_pget(L, 1);
    X509_NAME *name = X509_get_issuer_name(cert);
  
    lua_pushlightuserdata(L, name);
    luaL_getmetatable(L, LUACRYPTO_X509NAME);
    lua_setmetatable(L, -2);
    return 1;
  }
  
  static int x509_get_not_before(lua_State *L) {
    X509 *cert = x509_pget(L, 1);
    ASN1_TIME *time = X509_get_notBefore(cert);
  
    lua_pushlightuserdata(L, time);
    luaL_getmetatable(L, LUACRYPTO_ASN1TIME);
    lua_setmetatable(L, -2);
    return 1;
  }
  
  static int x509_get_not_after(lua_State *L) {
    X509 *cert = x509_pget(L, 1);
    ASN1_TIME *time = X509_get_notAfter(cert);
  
    lua_pushlightuserdata(L, time);
    luaL_getmetatable(L, LUACRYPTO_ASN1TIME);
    lua_setmetatable(L, -2);
    return 1;
  }
  
  static int x509_tostring(lua_State *L) {
    apr_pool_t *pool;
    X509 *cert = x509_pget(L, 1);
    char *s;
    HT_POOL_CREATE(&pool);
    s = apr_psprintf(pool, "X509 cert %p", (void *)cert);
    lua_pushstring(L, s);
    apr_pool_destroy(pool);
    return 1;
  }
  
  static int x509_gc(lua_State *L) {
    X509 *c = x509_pget(L, 1);
    X509_free(c);
    return 1;
  }
  
  /**
   * X509_NAME Object
   */
  static X509_NAME *x509_name_pget(lua_State *L, int i) {
    if (luaL_checkudata(L, i, LUACRYPTO_X509NAME) == NULL) {
      luaL_argerror(L, 1, "invalid object type");
    }
    return lua_touserdata(L, i);
  }
  
  static int x509_name_clone(lua_State *L) {
    X509_NAME *name = x509_name_pget(L, 1);
    X509_NAME *copy = X509_NAME_dup(name);
  
    lua_pushlightuserdata(L, copy);
    luaL_getmetatable(L, LUACRYPTO_X509NAME);
    lua_setmetatable(L, -2);
    return 1;
  }
  
  static int x509_name_tostring(lua_State *L) {
    char *s;
    X509_NAME *name = x509_name_pget(L, 1);
    s = X509_NAME_oneline(name, NULL, 0);
    lua_pushstring(L, s);
    OPENSSL_free(s);
    return 1;
  }
  
  static int x509_name_toasn1(lua_State *L) {
    unsigned char *s = NULL;
    apr_size_t len;
    X509_NAME *name = x509_name_pget(L, 1);
    len = i2d_X509_NAME(name, &s);
    lua_pushlstring(L, (char *)s, len);
    OPENSSL_free(s);
    return 1;
  }
  
  static int x509_name_gc(lua_State *L) {
    X509_NAME *name = x509_name_pget(L, 1);
    X509_NAME_free(name);
    return 1;
  }
  
  /**
   * ASN1_TIME Object
   */
  static ASN1_TIME *asn1_time_pget(lua_State *L, int i) {
    if (luaL_checkudata(L, i, LUACRYPTO_ASN1TIME) == NULL) {
      luaL_argerror(L, 1, "invalid object type");
    }
    return lua_touserdata(L, i);
  }
  
  static int asn1_time_fnew(lua_State *L) {
    ASN1_TIME *asn1time = ASN1_TIME_new();
    time_t t = time(NULL);
    ASN1_TIME_set(asn1time, t);
    lua_pushlightuserdata(L, asn1time);
    luaL_getmetatable(L, LUACRYPTO_ASN1TIME);
    lua_setmetatable(L, -2);
  
    return 1;
  }
  
  static int asn1_time_clone(lua_State *L) {
    ASN1_TIME *time = asn1_time_pget(L, 1);
    ASN1_TIME *copy = (ASN1_TIME *)ASN1_STRING_dup((ASN1_STRING *)time);
    lua_pushlightuserdata(L, copy);
    luaL_getmetatable(L, LUACRYPTO_ASN1TIME);
    lua_setmetatable(L, -2);
    return 1;
  }
  
  static int asn1_time_tostring(lua_State *L) {
    char s[1024];
    BIO *mem;
    ASN1_TIME *time = asn1_time_pget(L, 1);
    mem = BIO_new_mem_buf((void *)s, 1024);
    ASN1_TIME_print(mem, time);
    lua_pushstring(L, s);
    return 1;
  }
  
  static int asn1_time_toasn1(lua_State *L) {
    unsigned char *s = NULL;
    apr_size_t len;
    ASN1_TIME *time = asn1_time_pget(L, 1);
    len = i2d_ASN1_TIME(time, &s);
    lua_pushlstring(L, (char *)s, len);
    OPENSSL_free(s);
    return 1;
  }
  
  static int asn1_time_gc(lua_State *L) {
    ASN1_TIME *time = asn1_time_pget(L, 1);
    ASN1_TIME_free(time);
    return 1;
  }
  
  /**
   * DH object
   */
  
  static int dh_cb(int p, int n, BN_GENCB *cb) {
    char c='*';
  
    switch (p) {
    case 0:
      c='.';
      break;
    case 1:
      c='+';
      break;
    case 2:
      c='*';
      break;
    case 3:
      c='\n';
      break;
    }
    BIO_write(BN_GENCB_get_arg(cb),&c,1);
    (void)BIO_flush(BN_GENCB_get_arg(cb));
    return 1;
  }
  
  static DH *dh_pget(lua_State *L, int i) {
    if (luaL_checkudata(L, i, LUACRYPTO_DH) == NULL) {
      luaL_argerror(L, 1, "invalid object type");
    }
    return lua_touserdata(L, i);
  }
  
  static int dh_fnew(lua_State *L) {
    int generator = luaL_checknumber(L, 1);
    int num = luaL_checknumber(L, 2);
    DH *dh = DH_new();
    BN_GENCB *cb = BN_GENCB_new();
    BIO *bio_err;
    if ((bio_err = BIO_new(BIO_s_file())) != NULL) {
      BIO_set_fp(bio_err,stderr,BIO_NOCLOSE|BIO_FP_TEXT);
    }
    BN_GENCB_set(cb, dh_cb, bio_err);
    if (!DH_generate_parameters_ex(dh, num, generator, cb)) {
      luaL_argerror(L, 1, "could not generate DH paramters");
      return 1;
    }
    DH_generate_key(dh);
    BN_GENCB_free(cb);
  
    lua_pushlightuserdata(L, dh);
    luaL_getmetatable(L, LUACRYPTO_DH);
    lua_setmetatable(L, -2);
  
    return 1;
  }
  
  static int dh_clone(lua_State *L) {
    DH *dh = dh_pget(L, 1);
    DH *copy = DHparams_dup(dh);
  
    lua_pushlightuserdata(L, copy);
    luaL_getmetatable(L, LUACRYPTO_DH);
    lua_setmetatable(L, -2);
    return 1;
  }
  
  static int dh_tostring(lua_State *L) {
    char *s;
    apr_pool_t *pool;
    DH *dh = dh_pget(L, 1);
    HT_POOL_CREATE(&pool);
    s = apr_psprintf(pool, "DH %p", (void *)dh);
    lua_pushstring(L, s);
    apr_pool_destroy(pool);
    return 1;
  }
  
  static int dh_get_prime(lua_State *L) {
    apr_size_t len;
    unsigned char *s;
    apr_pool_t *pool;
    DH *dh = dh_pget(L, 1);
    HT_POOL_CREATE(&pool);
    s = apr_pcalloc(pool, DH_size(dh));
    len = BN_bn2bin(DH_get0_p(dh), s);
    lua_pushlstring(L, (char *)s, len);
    apr_pool_destroy(pool);
    return 1;
  }
  
  static int dh_get_priv_key(lua_State *L) {
    apr_size_t len;
    unsigned char *s;
    apr_pool_t *pool;
    DH *dh = dh_pget(L, 1);
    HT_POOL_CREATE(&pool);
    s = apr_pcalloc(pool, BN_num_bytes(DH_get0_priv_key(dh)));
    len = BN_bn2bin(DH_get0_priv_key(dh), s);
    lua_pushlstring(L, (char *)s, len);
    apr_pool_destroy(pool);
    return 1;
  }
  
  static int dh_get_pub_key(lua_State *L) {
    apr_size_t len;
    unsigned char *s;
    apr_pool_t *pool;
    DH *dh = dh_pget(L, 1);
    HT_POOL_CREATE(&pool);
    s = apr_pcalloc(pool, BN_num_bytes(DH_get0_pub_key(dh)));
    len = BN_bn2bin(DH_get0_pub_key(dh), s);
    lua_pushlstring(L, (char *)s, len);
    apr_pool_destroy(pool);
    return 1;
  }
  
  static int dh_gc(lua_State *L) {
    DH *dh = dh_pget(L, 1);
    DH_free(dh);
    return 1;
  }
  
  /**
   * Create a metatable and leave it on top of the stack.
   */
  int luacrypto_createmeta (lua_State *L, const char *name, const luaL_Reg *methods) {
    if (!luaL_newmetatable (L, name)) {
      return 0;
    }
    
    /* define methods */
    luaL_openlib (L, NULL, methods, 0);
    
    /* 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;
  }
  
  /**
   * Create metatables for each class of object.
   */
  static void create_metatables (lua_State *L) {
    struct luaL_Reg evp_functions[] = {
      { "digest", evp_fdigest },
      { "new", evp_fnew },
      {NULL, NULL},
    };
  
    struct luaL_Reg evp_methods[] = {
      { "__tostring", evp_tostring },
      { "__gc", evp_gc },
      { "clone", evp_clone },
      { "digest", evp_digest },
      { "reset", evp_reset },
      { "tostring", evp_tostring },
      { "update",	evp_update },
      {NULL, NULL},
    };
  
    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 },
      { "digest", hmac_digest },
      { "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 b64_functions[] = {
      { "encode", b64_encode },
      { "decode", b64_decode },
      {NULL, NULL},
    };
  
    struct luaL_Reg x509_functions[] = {
      { "new", x509_fnew },
      { "load", x509_fload },
      {NULL, NULL},
    };
  
    struct luaL_Reg x509_methods[] = {
      { "__tostring", x509_tostring },
      { "__gc", x509_gc },
      { "clone", x509_clone },
      { "tostring", x509_tostring },
      { "get_subject_name", x509_get_subject_name }, 
      { "get_issuer_name", x509_get_issuer_name }, 
      { "get_not_before", x509_get_not_before }, 
      { "get_not_after", x509_get_not_after }, 
      {NULL, NULL},
    };
  
    struct luaL_Reg x509_name_methods[] = {
      { "__tostring", x509_name_tostring },
      { "__gc", x509_name_gc },
      { "clone", x509_name_clone },
      { "tostring", x509_name_tostring },
      { "toasn1", x509_name_toasn1 },
      {NULL, NULL},
    };
  
    struct luaL_Reg asn1_time_functions[] = {
      { "new", asn1_time_fnew },
      {NULL, NULL},
    };
  
    struct luaL_Reg asn1_time_methods[] = {
      { "__tostring", asn1_time_tostring },
      { "__gc", asn1_time_gc },
      { "clone", asn1_time_clone },
      { "tostring", asn1_time_tostring },
      { "toasn1", asn1_time_toasn1 },
      {NULL, NULL},
    };
  
    struct luaL_Reg dh_functions[] = {
      { "new", dh_fnew },
      {NULL, NULL},
    };
  
    struct luaL_Reg dh_methods[] = {
      { "__tostring", dh_tostring },
      { "__gc", dh_gc },
      { "clone", dh_clone },
      { "tostring", dh_tostring },
      { "get_prime", dh_get_prime },
      { "get_priv_key", dh_get_priv_key },
      { "get_pub_key", dh_get_pub_key },
      {NULL, NULL},
    };
  
    luaL_openlib (L, LUACRYPTO_EVP, evp_functions, 0);
    luacrypto_createmeta(L, LUACRYPTO_EVP, evp_methods);
    luaL_openlib (L, LUACRYPTO_HMAC, hmac_functions, 0);
    luacrypto_createmeta(L, LUACRYPTO_HMAC, hmac_methods);
    luaL_openlib (L, LUACRYPTO_RAND, rand_functions, 0);
    luaL_openlib (L, LUACRYPTO_BASE64, b64_functions, 0);
    luaL_openlib (L, LUACRYPTO_X509, x509_functions, 0);
    luacrypto_createmeta(L, LUACRYPTO_X509, x509_methods);
    luacrypto_createmeta(L, LUACRYPTO_X509NAME, x509_name_methods);
    luaL_openlib (L, LUACRYPTO_ASN1TIME, asn1_time_functions, 0);
    luacrypto_createmeta(L, LUACRYPTO_ASN1TIME, asn1_time_methods);
    luaL_openlib (L, LUACRYPTO_DH, dh_functions, 0);
    luacrypto_createmeta(L, LUACRYPTO_DH, dh_methods);
    lua_pop (L, 3);
  }
  
  /**
   * Creates the metatables for the objects and registers the
   * driver open method.
   * @param L IN Lua hook
   * @return 1
   */
  int luaopen_crypto(lua_State *L) {
    struct luaL_Reg core[] = {
      {NULL, NULL},
    };
    OpenSSL_add_all_digests();
    create_metatables (L);
    luaL_openlib (L, LUACRYPTO_CORE, core, 0);
    return 1;
  }
  