diff options
Diffstat (limited to 'src/openssl.c')
-rw-r--r-- | src/openssl.c | 3929 |
1 files changed, 3929 insertions, 0 deletions
diff --git a/src/openssl.c b/src/openssl.c new file mode 100644 index 0000000..c8af43d --- /dev/null +++ b/src/openssl.c @@ -0,0 +1,3929 @@ +/* ========================================================================== + * openssl.c - Lua OpenSSL + * -------------------------------------------------------------------------- + * Copyright (c) 2012 William Ahern + * + * 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. + * ========================================================================== + */ +#ifndef L_OPENSSL_H +#define L_OPENSSH_H + +#include <limits.h> /* INT_MAX INT_MIN */ +#include <string.h> /* memset(3) */ +#include <strings.h> /* strcasecmp(3) */ +#include <math.h> /* INFINITY fabs(3) floor(3) frexp(3) fmod(3) round(3) isfinite(3) */ +#include <time.h> /* struct tm time_t strptime(3) */ + +#include <sys/types.h> +#include <sys/stat.h> /* struct stat stat(2) */ +#include <sys/socket.h> /* AF_INET AF_INET6 */ + +#include <netinet/in.h> /* struct in_addr struct in6_addr */ +#include <arpa/inet.h> /* inet_pton(3) */ + +#include <openssl/err.h> +#include <openssl/bn.h> +#include <openssl/asn1.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/evp.h> +#include <openssl/pem.h> +#include <openssl/ssl.h> +#include <openssl/hmac.h> +#include <openssl/rand.h> + +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> + +#if LUA_VERSION_NUM < 502 +#include "compat52.h" +#endif + +#define BIGNUM_CLASS "BIGNUM*" +#define PUBKEY_CLASS "EVP_PKEY*" +#define X509_NAME_CLASS "X509_NAME*" +#define X509_GENS_CLASS "GENERAL_NAMES*" +#define X509_CERT_CLASS "X509*" +#define X509_CHAIN_CLASS "STACK_OF(X509)*" +#define X509_CSR_CLASS "X509_REQ*" +#define X509_STORE_CLASS "X509_STORE*" +#define X509_STCTX_CLASS "X509_STORE_CTX*" +#define SSL_CTX_CLASS "SSL_CTX*" +#define SSL_CLASS "SSL*" +#define DIGEST_CLASS "EVP_MD_CTX" /* not a pointer */ +#define HMAC_CLASS "HMAC_CTX" /* not a pointer */ +#define CIPHER_CLASS "EVP_CIPHER_CTX" /* not a pointer */ + + +#define countof(a) (sizeof (a) / sizeof *(a)) +#define endof(a) (&(a)[countof(a)]) + +#define CLAMP(i, min, max) (((i) < (min))? (min) : ((i) > (max))? (max) : (i)) + +#undef MIN +#define MIN(a, b) (((a) < (b))? (a) : (b)) + +#define stricmp(a, b) strcasecmp((a), (b)) +#define strieq(a, b) (!stricmp((a), (b))) + +#define SAY_(file, func, line, fmt, ...) \ + fprintf(stderr, "%s:%d: " fmt "%s", __func__, __LINE__, __VA_ARGS__) + +#define SAY(...) SAY_(__FILE__, __func__, __LINE__, __VA_ARGS__, "\n") + +#define HAI SAY("hai") + + +static void *prepudata(lua_State *L, size_t size, const char *tname, int (*gc)(lua_State *)) { + void *p = memset(lua_newuserdata(L, size), 0, size); + + if (tname) { + luaL_setmetatable(L, tname); + } else { + lua_newtable(L); + lua_pushcfunction(L, gc); + lua_setfield(L, -2, "__gc"); + lua_setmetatable(L, -2); + } + + return p; +} /* prepudata() */ + + +static void *prepsimple(lua_State *L, const char *tname, int (*gc)(lua_State *)) { + void **p = prepudata(L, sizeof (void *), tname, gc); + return p; +} /* prepsimple() */ + +#define prepsimple_(a, b, c, ...) prepsimple((a), (b), (c)) +#define prepsimple(...) prepsimple_(__VA_ARGS__, 0) + + +static void *checksimple(lua_State *L, int index, const char *tname) { + void **p; + + if (tname) { + p = luaL_checkudata(L, index, tname); + } else { + luaL_checktype(L, index, LUA_TUSERDATA); + p = lua_touserdata(L, index); + } + + return *p; +} /* checksimple() */ + + +static void *testsimple(lua_State *L, int index, const char *tname) { + void **p; + + if (tname) { + p = luaL_testudata(L, index, tname); + } else { + luaL_checktype(L, index, LUA_TUSERDATA); + p = lua_touserdata(L, index); + } + + return (p)? *p : (void *)0; +} /* testsimple() */ + + +static const char *pusherror(lua_State *L, const char *fun) { + unsigned long code; + const char *path, *file; + int line; + char txt[256]; + + code = ERR_get_error_line(&path, &line); + + if ((file = strrchr(path, '/'))) + ++file; + else + file = path; + + ERR_clear_error(); + + ERR_error_string_n(code, txt, sizeof txt); + + if (fun) + return lua_pushfstring(L, "%s: %s:%d:%s", fun, file, line, txt); + else + return lua_pushfstring(L, "%s:%d:%s", file, line, txt); +} /* pusherror() */ + + +static int throwssl(lua_State *L, const char *fun) { + pusherror(L, fun); + + return lua_error(L); +} /* throwssl() */ + + +static int interpose(lua_State *L, const char *mt) { + luaL_getmetatable(L, mt); + + if (!strncmp("__", luaL_checkstring(L, 1), 2)) + lua_pushvalue(L, -1); + else + lua_getfield(L, -1, "__index"); + + lua_pushvalue(L, -4); /* push method name */ + lua_gettable(L, -2); /* push old method */ + + lua_pushvalue(L, -5); /* push method name */ + lua_pushvalue(L, -5); /* push new method */ + lua_settable(L, -4); /* replace old method */ + + return 1; /* return old method */ +} /* interpose() */ + + +static void addclass(lua_State *L, const char *name, const luaL_Reg *methods, const luaL_Reg *metamethods) { + if (luaL_newmetatable(L, name)) { + luaL_setfuncs(L, metamethods, 0); + lua_newtable(L); + luaL_setfuncs(L, methods, 0); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); + } +} /* addclass() */ + + +static int checkoption(struct lua_State *L, int index, const char *def, const char *opts[]) { + const char *opt = (def)? luaL_optstring(L, index, def) : luaL_checkstring(L, index); + int i; + + for (i = 0; opts[i]; i++) { + if (strieq(opts[i], opt)) + return i; + } + + return luaL_argerror(L, index, lua_pushfstring(L, "invalid option %s", opt)); +} /* checkoption() */ + + +static _Bool getfield(lua_State *L, int index, const char *k) { + lua_getfield(L, index, k); + + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + + return 0; + } else { + return 1; + } +} /* getfield() */ + + +static _Bool loadfield(lua_State *L, int index, const char *k, int type, void *p) { + if (!getfield(L, index, k)) + return 0; + + switch (type) { + case LUA_TSTRING: + *(const char **)p = luaL_checkstring(L, -1); + break; + case LUA_TNUMBER: + *(lua_Number *)p = luaL_checknumber(L, -1); + break; + default: + luaL_error(L, "loadfield(type=%d): invalid type", type); + break; + } /* switch() */ + + lua_pop(L, 1); /* table keeps reference */ + + return 1; +} /* loadfield() */ + + +static const char *pushnid(lua_State *L, int nid) { + const char *txt; + ASN1_OBJECT *obj; + char buf[256]; + int len; + + if ((txt = OBJ_nid2sn(nid)) || (txt = OBJ_nid2ln(nid))) { + lua_pushstring(L, txt); + } else { + if (!(obj = OBJ_nid2obj(nid))) + luaL_error(L, "%d: unknown ASN.1 NID", nid); + + if (-1 == (len = OBJ_obj2txt(buf, sizeof buf, obj, 1))) + luaL_error(L, "%d: invalid ASN.1 NID", nid); + + lua_pushlstring(L, buf, len); + } + + return lua_tostring(L, -1); +} /* pushnid() */ + + +static void initall(lua_State *L); + + +/* + * BIGNUM - openssl.bignum + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static BIGNUM *bn_push(lua_State *L) { + BIGNUM **ud = prepsimple(L, BIGNUM_CLASS); + + if (!(*ud = BN_new())) + throwssl(L, "bignum.new"); + + return *ud; +} /* bn_push() */ + + +#define checkbig_(a, b, c, ...) checkbig((a), (b), (c)) +#define checkbig(...) checkbig_(__VA_ARGS__, &(_Bool){ 0 }) + +static BIGNUM *(checkbig)(lua_State *, int, _Bool *); + +static int bn_new(lua_State *L) { + int i, n; + + if ((n = lua_gettop(L)) > 0) { + for (i = 1; i <= n; i++) + checkbig(L, i); + + return n; + } else { + bn_push(L); + + return 1; + } +} /* bn_new() */ + + +static int bn_interpose(lua_State *L) { + return interpose(L, BIGNUM_CLASS); +} /* bn_interpose() */ + + +/* return integral part */ +static inline double intof(double f) { + return (isfinite(f))? floor(fabs(f)) : 0.0; +} /* intof() */ + + +/* convert integral to BN_ULONG. returns success or failure. */ +static _Bool int2ul(BN_ULONG *ul, double f) { + int exp; + + frexp(f, &exp); + + if (exp > (int)sizeof *ul * 8) + return 0; + + *ul = (BN_ULONG)f; + + return 1; +} /* int2ul() */ + + +/* convert integral BIGNUM. returns success or failure. */ +static _Bool int2bn(BIGNUM **bn, double q) { + unsigned char nib[32], bin[32], *p; + size_t i, n; + double r; + + p = nib; + + while (q >= 1.0 && p < endof(nib)) { + r = fmod(q, 256.0); + *p++ = r; + q = round((q - r) / 256.0); + } + + n = p - nib; + + for (i = 0; i < n; i++) { + bin[i] = *--p; + } + + if (!(*bn = BN_bin2bn(bin, n, *bn))) + return 0; + + return 1; +} /* int2bn() */ + + +/* convert double to BIGNUM. returns success or failure. */ +static _Bool f2bn(BIGNUM **bn, double f) { + double i = intof(f); + BN_ULONG lu; + + if (int2ul(&lu, i)) { + if (!*bn && !(*bn = BN_new())) + return 0; + + if (!BN_set_word(*bn, lu)) + return 0; + } else if (!int2bn(bn, i)) + return 0; + + BN_set_negative(*bn, signbit(f)); + + return 1; +} /* f2bn() */ + + +static BIGNUM *(checkbig)(lua_State *L, int index, _Bool *lvalue) { + BIGNUM **bn; + const char *dec; + size_t len; + + index = lua_absindex(L, index); + + switch (lua_type(L, index)) { + case LUA_TSTRING: + *lvalue = 0; + + dec = lua_tolstring(L, index, &len); + + luaL_argcheck(L, len > 0 && *dec, index, "invalid big number string"); + + bn = prepsimple(L, BIGNUM_CLASS); + + if (!BN_dec2bn(bn, dec)) + throwssl(L, "bignum"); + + lua_replace(L, index); + + return *bn; + case LUA_TNUMBER: + *lvalue = 0; + + bn = prepsimple(L, BIGNUM_CLASS); + + if (!f2bn(bn, lua_tonumber(L, index))) + throwssl(L, "bignum"); + + lua_replace(L, index); + + return *bn; + default: + *lvalue = 1; + + return checksimple(L, index, BIGNUM_CLASS); + } /* switch() */ +} /* checkbig() */ + + +static void bn_prepops(lua_State *L, BIGNUM **r, BIGNUM **a, BIGNUM **b, _Bool commute) { + _Bool lvalue = 1; + + lua_settop(L, 2); /* a, b */ + + *a = checkbig(L, 1, &lvalue); + + if (!lvalue && commute) + lua_pushvalue(L, 1); + + *b = checkbig(L, 2, &lvalue); + + if (!lvalue && commute && lua_gettop(L) < 3) + lua_pushvalue(L, 2); + + if (lua_gettop(L) < 3) + bn_push(L); + + *r = *(BIGNUM **)lua_touserdata(L, 3); +} /* bn_prepops() */ + + +static int ctx__gc(lua_State *L) { + BN_CTX **ctx = lua_touserdata(L, 1); + + BN_CTX_free(*ctx); + *ctx = NULL; + + return 0; +} /* ctx__gc() */ + +static BN_CTX *getctx(lua_State *L) { + BN_CTX **ctx; + + lua_pushcfunction(L, &ctx__gc); + lua_gettable(L, LUA_REGISTRYINDEX); + + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + + ctx = prepsimple(L, NULL, &ctx__gc); + + if (!(*ctx = BN_CTX_new())) + throwssl(L, "bignum"); + + lua_pushcfunction(L, &ctx__gc); + lua_pushvalue(L, -2); + lua_settable(L, LUA_REGISTRYINDEX); + } + + ctx = lua_touserdata(L, -1); + lua_pop(L, 1); + + return *ctx; +} /* getctx() */ + + +static int bn__add(lua_State *L) { + BIGNUM *r, *a, *b; + + bn_prepops(L, &r, &a, &b, 1); + + if (!BN_add(r, a, b)) + return throwssl(L, "bignum:__add"); + + return 1; +} /* bn__add() */ + + +static int bn__sub(lua_State *L) { + BIGNUM *r, *a, *b; + + bn_prepops(L, &r, &a, &b, 0); + + if (!BN_sub(r, a, b)) + return throwssl(L, "bignum:__sub"); + + return 1; +} /* bn__sub() */ + + +static int bn__mul(lua_State *L) { + BIGNUM *r, *a, *b; + + bn_prepops(L, &r, &a, &b, 1); + + if (!BN_mul(r, a, b, getctx(L))) + return throwssl(L, "bignum:__mul"); + + return 1; +} /* bn__mul() */ + + +static int bn__div(lua_State *L) { + BIGNUM *r, *a, *b; + BN_CTX *ctx; + + bn_prepops(L, &r, &a, &b, 0); + + if (!BN_div(r, NULL, a, b, getctx(L))) + return throwssl(L, "bignum:__div"); + + return 1; +} /* bn__div() */ + + +static int bn__mod(lua_State *L) { + BIGNUM *r, *a, *b; + BN_CTX *ctx; + + bn_prepops(L, &r, &a, &b, 0); + + if (!BN_mod(r, a, b, getctx(L))) + return throwssl(L, "bignum:__mod"); + + return 1; +} /* bn__mod() */ + + +static int bn__pow(lua_State *L) { + BIGNUM *r, *a, *b; + BN_CTX *ctx; + + bn_prepops(L, &r, &a, &b, 0); + + if (!BN_exp(r, a, b, getctx(L))) + return throwssl(L, "bignum:__pow"); + + return 1; +} /* bn__pow() */ + + +static int bn__unm(lua_State *L) { + BIGNUM *a = checksimple(L, 1, BIGNUM_CLASS); + + BN_set_negative(a, !BN_is_negative(a)); + + return 1; +} /* bn__unm() */ + + +static int bn__eq(lua_State *L) { + BIGNUM *a = checksimple(L, 1, BIGNUM_CLASS); + BIGNUM *b = checksimple(L, 2, BIGNUM_CLASS); + + lua_pushboolean(L, 0 == BN_cmp(a, b)); + + return 1; +} /* bn__eq() */ + + +static int bn__lt(lua_State *L) { + BIGNUM *a = checksimple(L, 1, BIGNUM_CLASS); + BIGNUM *b = checksimple(L, 2, BIGNUM_CLASS); + int cmp = BN_cmp(a, b); + + lua_pushboolean(L, cmp == -1); + + return 1; +} /* bn__lt() */ + + +static int bn__le(lua_State *L) { + BIGNUM *a = checksimple(L, 1, BIGNUM_CLASS); + BIGNUM *b = checksimple(L, 2, BIGNUM_CLASS); + int cmp = BN_cmp(a, b); + + lua_pushboolean(L, cmp <= 0); + + return 1; +} /* bn__le() */ + + +static int bn__gc(lua_State *L) { + BIGNUM **ud = luaL_checkudata(L, 1, BIGNUM_CLASS); + + BN_free(*ud); + *ud = NULL; + + return 0; +} /* bn__gc() */ + + +static int bn__tostring(lua_State *L) { + BIGNUM *bn = checksimple(L, 1, BIGNUM_CLASS); + char *txt; + + if (!(txt = BN_bn2dec(bn))) + throwssl(L, "bignum:__tostring"); + + lua_pushstring(L, txt); + + return 1; +} /* bn__tostring() */ + + +static const luaL_Reg bn_methods[] = { + { NULL, NULL }, +}; + +static const luaL_Reg bn_metatable[] = { + { "__add", &bn__add }, + { "__sub", &bn__sub }, + { "__mul", &bn__mul }, + { "__div", &bn__div }, + { "__mod", &bn__mod }, + { "__pow", &bn__pow }, + { "__unm", &bn__unm }, + { "__eq", &bn__eq }, + { "__lt", &bn__lt }, + { "__le", &bn__le }, + { "__gc", &bn__gc }, + { "__tostring", &bn__tostring }, + { NULL, NULL }, +}; + + +static const luaL_Reg bn_globals[] = { + { "new", &bn_new }, + { "interpose", &bn_interpose }, + { NULL, NULL }, +}; + +int luaopen__openssl_bignum(lua_State *L) { + initall(L); + + luaL_newlib(L, bn_globals); + + return 1; +} /* luaopen__openssl_bignum() */ + + +/* + * EVP_PKEY - openssl.pubkey + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static int bio__gc(lua_State *L) { + BIO **bio = lua_touserdata(L, 1); + + BIO_free(*bio); + *bio = NULL; + + return 0; +} /* bio__gc() */ + +static BIO *getbio(lua_State *L) { + BIO **bio; + + lua_pushcfunction(L, &bio__gc); + lua_gettable(L, LUA_REGISTRYINDEX); + + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + + bio = prepsimple(L, NULL, &bio__gc); + + if (!(*bio = BIO_new(BIO_s_mem()))) + throwssl(L, "BIO_new"); + + lua_pushcfunction(L, &bio__gc); + lua_pushvalue(L, -2); + lua_settable(L, LUA_REGISTRYINDEX); + } + + bio = lua_touserdata(L, -1); + lua_pop(L, 1); + + BIO_reset(*bio); + + return *bio; +} /* getbio() */ + + +static int pk_new(lua_State *L) { + EVP_PKEY **ud; + + lua_settop(L, 1); + + ud = prepsimple(L, PUBKEY_CLASS); + + if (lua_istable(L, 1)) { + int type = EVP_PKEY_RSA; + unsigned bits = 1024; + unsigned exp = 65537; + int curve = NID_X9_62_prime192v1; + const char *id; + lua_Number n; + + if (!lua_istable(L, 1)) + goto creat; + + if (loadfield(L, 1, "type", LUA_TSTRING, &id)) { + static const struct { int nid; const char *sn; } types[] = { + { EVP_PKEY_RSA, "RSA" }, + { EVP_PKEY_DSA, "DSA" }, + { EVP_PKEY_DH, "DH" }, + { EVP_PKEY_EC, "EC" }, + }; + unsigned i; + + type = OBJ_sn2nid(id); + + if (NID_undef == (type = EVP_PKEY_type(OBJ_sn2nid(id)))) { + for (i = 0; i < countof(types); i++) { + if (strieq(id, types[i].sn)) { + type = types[i].nid; + break; + } + } + } + + luaL_argcheck(L, type != NID_undef, 1, lua_pushfstring(L, "%s: invalid key type", id)); + } + + if (loadfield(L, 1, "bits", LUA_TNUMBER, &n)) { + luaL_argcheck(L, n > 0 && n < UINT_MAX, 1, lua_pushfstring(L, "%f: `bits' invalid", n)); + bits = (unsigned)n; + } + + if (loadfield(L, 1, "exp", LUA_TNUMBER, &n)) { + luaL_argcheck(L, n > 0 && n < UINT_MAX, 1, lua_pushfstring(L, "%f: `exp' invalid", n)); + exp = (unsigned)n; + } + + if (loadfield(L, 1, "curve", LUA_TSTRING, &id)) { + curve = OBJ_sn2nid(id); + luaL_argcheck(L, curve != NID_undef, 1, lua_pushfstring(L, "%s: invalid curve", id)); + } + +creat: + if (!(*ud = EVP_PKEY_new())) + return throwssl(L, "pubkey.new"); + + switch (EVP_PKEY_type(type)) { + case EVP_PKEY_RSA: { + RSA *rsa; + + if (!(rsa = RSA_generate_key(bits, exp, 0, 0))) + return throwssl(L, "pubkey.new"); + + EVP_PKEY_set1_RSA(*ud, rsa); + + RSA_free(rsa); + + break; + } + case EVP_PKEY_DSA: { + DSA *dsa; + + if (!(dsa = DSA_generate_parameters(bits, 0, 0, 0, 0, 0, 0))) + return throwssl(L, "pubkey.new"); + + if (!DSA_generate_key(dsa)) { + DSA_free(dsa); + return throwssl(L, "pubkey.new"); + } + + EVP_PKEY_set1_DSA(*ud, dsa); + + DSA_free(dsa); + + break; + } + case EVP_PKEY_DH: { + DH *dh; + + if (!(dh = DH_generate_parameters(bits, exp, 0, 0))) + return throwssl(L, "pubkey.new"); + + if (!DH_generate_key(dh)) { + DH_free(dh); + return throwssl(L, "pubkey.new"); + } + + EVP_PKEY_set1_DH(*ud, dh); + + DH_free(dh); + + break; + } +#ifndef OPENSSL_NO_EC + case EVP_PKEY_EC: { + EC_GROUP *grp; + EC_KEY *key; + + if (!(grp = EC_GROUP_new_by_curve_name(curve))) + return throwssl(L, "pubkey.new"); + + EC_GROUP_set_asn1_flag(grp, OPENSSL_EC_NAMED_CURVE); + + /* compressed points patented */ + EC_GROUP_set_point_conversion_form(grp, POINT_CONVERSION_UNCOMPRESSED); + + if (!(key = EC_KEY_new())) { + EC_GROUP_free(grp); + return throwssl(L, "pubkey.new"); + } + + EC_KEY_set_group(key, grp); + + EC_GROUP_free(grp); + + if (!EC_KEY_generate_key(key)) { + EC_KEY_free(key); + return throwssl(L, "pubkey.new"); + } + + EVP_PKEY_set1_EC_KEY(*ud, key); + + EC_KEY_free(key); + + break; + } +#endif + default: + return luaL_error(L, "%d: unknown EVP base type (%d)", EVP_PKEY_type(type), type); + } /* switch() */ + } else { + const char *pem; + size_t len; + BIO *bio; + int ok; + + if (!(*ud = EVP_PKEY_new())) + return throwssl(L, "pubkey.new"); + + switch (lua_type(L, 1)) { + case LUA_TSTRING: + pem = luaL_checklstring(L, 1, &len); + + if (!(bio = BIO_new_mem_buf((void *)pem, len))) + return throwssl(L, "pubkey.new"); + + if (strstr(pem, "PUBLIC KEY")) { + ok = !!PEM_read_bio_PUBKEY(bio, ud, 0, 0); + } else { + ok = !!PEM_read_bio_PrivateKey(bio, ud, 0, 0); + } + + BIO_free(bio); + + if (!ok) + return throwssl(L, "pubkey.new"); + + break; + default: + return luaL_error(L, "%s: unknown key initializer", lua_typename(L, lua_type(L, 1))); + } /* switch() */ + } + + return 1; +} /* pk_new() */ + + +static int pk_interpose(lua_State *L) { + return interpose(L, X509_NAME_CLASS); +} /* pk_interpose() */ + + +static int pk_type(lua_State *L) { + EVP_PKEY *key = checksimple(L, 1, PUBKEY_CLASS); + int nid = key->type; + + pushnid(L, nid); + + return 1; +} /* pk_type() */ + + +static int pk_setPublicKey(lua_State *L) { + EVP_PKEY **key = luaL_checkudata(L, 1, PUBKEY_CLASS); + const char *pem; + size_t len; + BIO *bio; + int ok; + + lua_settop(L, 2); + + pem = luaL_checklstring(L, 2, &len); + + if (!(bio = BIO_new_mem_buf((void *)pem, len))) + return throwssl(L, "pubkey.new"); + + ok = !!PEM_read_bio_PUBKEY(bio, key, 0, 0); + + BIO_free(bio); + + if (!ok) + return throwssl(L, "pubkey.new"); + + lua_pushboolean(L, 1); + + return 1; +} /* pk_setPublicKey() */ + + +static int pk_setPrivateKey(lua_State *L) { + EVP_PKEY **key = luaL_checkudata(L, 1, PUBKEY_CLASS); + const char *pem; + size_t len; + BIO *bio; + int ok; + + lua_settop(L, 2); + + pem = luaL_checklstring(L, 2, &len); + + if (!(bio = BIO_new_mem_buf((void *)pem, len))) + return throwssl(L, "pubkey.new"); + + ok = !!PEM_read_bio_PrivateKey(bio, key, 0, 0); + + BIO_free(bio); + + if (!ok) + return throwssl(L, "pubkey.new"); + + lua_pushboolean(L, 1); + + return 1; +} /* pk_setPrivateKEY() */ + + +static int pk_sign(lua_State *L) { + EVP_PKEY *key = checksimple(L, 1, PUBKEY_CLASS); + EVP_MD_CTX *md = luaL_checkudata(L, 2, DIGEST_CLASS); + luaL_Buffer B; + unsigned n; + + if (LUAL_BUFFERSIZE < EVP_PKEY_size(key)) + return luaL_error(L, "pubkey:sign: LUAL_BUFFERSIZE(%u) < EVP_PKEY_size(%u)", (unsigned)LUAL_BUFFERSIZE, (unsigned)EVP_PKEY_size(key)); + + luaL_buffinit(L, &B); + n = LUAL_BUFFERSIZE; + + if (!EVP_SignFinal(md, (void *)luaL_prepbuffer(&B), &n, key)) + return throwssl(L, "pubkey:sign"); + + luaL_addsize(&B, n); + luaL_pushresult(&B); + + return 1; +} /* pk_sign() */ + + +static int pk_verify(lua_State *L) { + EVP_PKEY *key = checksimple(L, 1, PUBKEY_CLASS); + size_t len; + const void *sig = luaL_checklstring(L, 2, &len); + EVP_MD_CTX *md = luaL_checkudata(L, 3, DIGEST_CLASS); + + switch (EVP_VerifyFinal(md, sig, len, key)) { + case 0: /* WRONG */ + ERR_clear_error(); + lua_pushboolean(L, 0); + + break; + case 1: /* OK */ + lua_pushboolean(L, 1); + + break; + default: + return throwssl(L, "pubkey:verify"); + } + + return 1; +} /* pk_verify() */ + + +static int pk_toPEM(lua_State *L) { + EVP_PKEY *key = checksimple(L, 1, PUBKEY_CLASS); + int top, i, ok; + BIO *bio; + char *pem; + long len; + + if (1 == (top = lua_gettop(L))) { + lua_pushstring(L, "publickey"); + ++top; + } + + bio = getbio(L); + + for (i = 2; i <= top; i++) { + static const char *opts[] = { + "public", "PublicKey", + "private", "PrivateKey", +// "params", "Parameters", + NULL, + }; + + switch (checkoption(L, i, NULL, opts)) { + case 0: case 1: /* public, PublicKey */ + if (!PEM_write_bio_PUBKEY(bio, key)) + return throwssl(L, "pubkey:__tostring"); + + len = BIO_get_mem_data(bio, &pem); + lua_pushlstring(L, pem, len); + + BIO_reset(bio); + break; + case 2: case 3: /* private, PrivateKey */ + if (!PEM_write_bio_PrivateKey(bio, key, 0, 0, 0, 0, 0)) + throwssl(L, "pubkey:__tostring"); + + len = BIO_get_mem_data(bio, &pem); + lua_pushlstring(L, pem, len); + + break; +#if 0 + case 4: case 5: /* params, Parameters */ + /* EVP_PKEY_base_id not in OS X */ + switch (EVP_PKEY_type(key->type)) { + case EVP_PKEY_RSA: + break; + case EVP_PKEY_DSA: { + DSA *dsa = EVP_PKEY_get1_DSA(key); + + ok = !!PEM_write_bio_DSAparams(bio, dsa); + + DSA_free(dsa); + + if (!ok) + return throwssl(L, "pubkey:__tostring"); + + break; + } + case EVP_PKEY_DH: { + DH *dh = EVP_PKEY_get1_DH(key); + + ok = !!PEM_write_bio_DHparams(bio, dh); + + DH_free(dh); + + if (!ok) + return throwssl(L, "pubkey:__tostring"); + + break; + } +#ifndef OPENSSL_NO_EC + case EVP_PKEY_EC: { + EC_KEY *ec = EVP_PKEY_get1_EC_KEY(key); + const EC_GROUP *grp = EC_KEY_get0_group(ec); + + ok = !!PEM_write_bio_ECPKParameters(bio, grp); + + EC_KEY_free(ec); + + if (!ok) + return throwssl(L, "pubkey:__tostring"); + + break; + } +#endif + default: + return luaL_error(L, "%d: unknown EVP base type", EVP_PKEY_type(key->type)); + } + + lua_pushlstring(L, pem, len); + + BIO_reset(bio); + + break; +#endif + default: + lua_pushnil(L); + + break; + } /* switch() */ + } /* for() */ + + return lua_gettop(L) - top; +} /* pk_toPEM() */ + + +static int pk__tostring(lua_State *L) { + EVP_PKEY *key = checksimple(L, 1, PUBKEY_CLASS); + BIO *bio = getbio(L); + char *pem; + long len; + int ok; + + if (!PEM_write_bio_PUBKEY(bio, key)) + return throwssl(L, "pubkey:__tostring"); + + len = BIO_get_mem_data(bio, &pem); + lua_pushlstring(L, pem, len); + + return 1; +} /* pk__tostring() */ + + +static int pk__gc(lua_State *L) { + EVP_PKEY **ud = luaL_checkudata(L, 1, PUBKEY_CLASS); + + EVP_PKEY_free(*ud); + *ud = NULL; + + return 0; +} /* pk__gc() */ + + +static const luaL_Reg pk_methods[] = { + { "type", &pk_type }, + { "setPublicKey", &pk_setPublicKey }, + { "setPrivateKey", &pk_setPrivateKey }, + { "sign", &pk_sign }, + { "verify", &pk_verify }, + { "toPEM", &pk_toPEM }, + { NULL, NULL }, +}; + +static const luaL_Reg pk_metatable[] = { + { "__tostring", &pk__tostring }, + { "__gc", &pk__gc }, + { NULL, NULL }, +}; + + +static const luaL_Reg pk_globals[] = { + { "new", &pk_new }, + { "interpose", &pk_interpose }, + { NULL, NULL }, +}; + +int luaopen__openssl_pubkey(lua_State *L) { + initall(L); + + luaL_newlib(L, pk_globals); + + return 1; +} /* luaopen__openssl_pubkey() */ + + +/* + * X509_NAME - openssl.x509.name + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static X509_NAME *xn_dup(lua_State *L, X509_NAME *name) { + X509_NAME **ud = prepsimple(L, X509_NAME_CLASS); + + if (!(*ud = X509_NAME_dup(name))) + throwssl(L, "x509.name.dup"); + + return *ud; +} /* xn_dup() */ + + +static int xn_new(lua_State *L) { + X509_NAME **ud = prepsimple(L, X509_NAME_CLASS); + + if (!(*ud = X509_NAME_new())) + return throwssl(L, "x509.name.new"); + + return 1; +} /* xn_new() */ + + +static int xn_interpose(lua_State *L) { + return interpose(L, X509_NAME_CLASS); +} /* xn_interpose() */ + + +static int xn_add(lua_State *L) { + X509_NAME *name = checksimple(L, 1, X509_NAME_CLASS); + const char *nid = luaL_checkstring(L, 2); + size_t len; + const char *txt = luaL_checklstring(L, 3, &len); + ASN1_OBJECT *obj; + int ok; + + if (!(obj = OBJ_txt2obj(nid, 0))) + return luaL_error(L, "x509.name:add: %s: invalid NID", nid); + + ok = !!X509_NAME_add_entry_by_OBJ(name, obj, MBSTRING_ASC, (unsigned char *)txt, len, -1, 0); + + ASN1_OBJECT_free(obj); + + if (!ok) + return throwssl(L, "x509.name:add"); + + lua_pushboolean(L, 1); + + return 1; +} /* xn_add() */ + + +static int xn_all(lua_State *L) { + X509_NAME *name = checksimple(L, 1, X509_NAME_CLASS); + int count = X509_NAME_entry_count(name); + X509_NAME_ENTRY *entry; + ASN1_OBJECT *obj; + const char *id; + char txt[256]; + int i, nid, len; + + lua_newtable(L); + + for (i = 0; i < count; i++) { + if (!(entry = X509_NAME_get_entry(name, i))) + continue; + + lua_newtable(L); + + obj = X509_NAME_ENTRY_get_object(entry); + nid = OBJ_obj2nid(obj); + + if (0 > (len = OBJ_obj2txt(txt, sizeof txt, obj, 1))) + return throwssl(L, "x509.name:all"); + + lua_pushlstring(L, txt, len); + + if (nid != NID_undef && ((id = OBJ_nid2ln(nid)) || (id = OBJ_nid2sn(nid)))) + lua_pushstring(L, id); + else + lua_pushvalue(L, -1); + + if (nid != NID_undef && (id = OBJ_nid2sn(nid))) + lua_pushstring(L, id); + else + lua_pushvalue(L, -1); + + lua_setfield(L, -4, "sn"); + lua_setfield(L, -3, "ln"); + lua_setfield(L, -2, "id"); + + len = ASN1_STRING_length(X509_NAME_ENTRY_get_data(entry)); + lua_pushlstring(L, (char *)ASN1_STRING_data(X509_NAME_ENTRY_get_data(entry)), len); + + lua_setfield(L, -2, "blob"); + + lua_rawseti(L, -2, i + 1); + } + + return 1; +} /* xn_all() */ + + +static int xn__next(lua_State *L) { + X509_NAME *name = checksimple(L, lua_upvalueindex(1), X509_NAME_CLASS); + X509_NAME_ENTRY *entry; + ASN1_OBJECT *obj; + const char *id; + char txt[256]; + int i, n, nid, len; + + lua_settop(L, 0); + + i = lua_tointeger(L, lua_upvalueindex(2)); + n = X509_NAME_entry_count(name); + + while (i < n) { + if (!(entry = X509_NAME_get_entry(name, i++))) + continue; + + obj = X509_NAME_ENTRY_get_object(entry); + nid = OBJ_obj2nid(obj); + + if (nid != NID_undef && ((id = OBJ_nid2sn(nid)) || (id = OBJ_nid2ln(nid)))) { + lua_pushstring(L, id); + } else { + if (0 > (len = OBJ_obj2txt(txt, sizeof txt, obj, 1))) + return throwssl(L, "x509.name:__pairs"); + + lua_pushlstring(L, txt, len); + } + + len = ASN1_STRING_length(X509_NAME_ENTRY_get_data(entry)); + lua_pushlstring(L, (char *)ASN1_STRING_data(X509_NAME_ENTRY_get_data(entry)), len); + + break; + } + + lua_pushinteger(L, i); + lua_replace(L, lua_upvalueindex(2)); + + return lua_gettop(L); +} /* xn__next() */ + +static int xn__pairs(lua_State *L) { + lua_settop(L, 1); + lua_pushinteger(L, 0); + + lua_pushcclosure(L, &xn__next, 2); + + return 1; +} /* xn__pairs() */ + + +static int xn__gc(lua_State *L) { + X509_NAME **ud = luaL_checkudata(L, 1, X509_NAME_CLASS); + + X509_NAME_free(*ud); + *ud = NULL; + + return 0; +} /* xn__gc() */ + + +static int xn__tostring(lua_State *L) { + X509_NAME *name = checksimple(L, 1, X509_NAME_CLASS); + char txt[1024] = { 0 }; + + /* FIXME: oneline is deprecated */ + X509_NAME_oneline(name, txt, sizeof txt); + + lua_pushstring(L, txt); + + return 1; +} /* xn__tostring() */ + + +static const luaL_Reg xn_methods[] = { + { "add", &xn_add }, + { "all", &xn_all }, + { NULL, NULL }, +}; + +static const luaL_Reg xn_metatable[] = { + { "__pairs", &xn__pairs }, + { "__gc", &xn__gc }, + { "__tostring", &xn__tostring }, + { NULL, NULL }, +}; + + +static const luaL_Reg xn_globals[] = { + { "new", &xn_new }, + { "interpose", &xn_interpose }, + { NULL, NULL }, +}; + +int luaopen__openssl_x509_name(lua_State *L) { + initall(L); + + luaL_newlib(L, xn_globals); + + return 1; +} /* luaopen__openssl_x509_name() */ + + +/* + * GENERAL_NAMES - openssl.x509.altname + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static GENERAL_NAMES *gn_dup(lua_State *L, GENERAL_NAMES *gens) { + GENERAL_NAMES **ud = prepsimple(L, X509_GENS_CLASS); + + if (!(*ud = sk_GENERAL_NAME_dup(gens))) + throwssl(L, "x509.altname.dup"); + + return *ud; +} /* gn_dup() */ + + +static int gn_new(lua_State *L) { + GENERAL_NAMES **ud = prepsimple(L, X509_GENS_CLASS); + + if (!(*ud = sk_GENERAL_NAME_new_null())) + return throwssl(L, "x509.altname.new"); + + return 1; +} /* gn_new() */ + + +static int gn_interpose(lua_State *L) { + return interpose(L, X509_GENS_CLASS); +} /* gn_interpose() */ + + +static int gn_setCritical(lua_State *L) { + GENERAL_NAMES *gens = checksimple(L, 1, X509_GENS_CLASS); + + return 0; +} /* gn_setCritical() */ + + +static int gn_checktype(lua_State *L, int index) { + static const struct { int type; const char *name; } table[] = { + { GEN_EMAIL, "RFC822Name" }, + { GEN_EMAIL, "RFC822" }, + { GEN_EMAIL, "email" }, + { GEN_URI, "UniformResourceIdentifier" }, + { GEN_URI, "URI" }, + { GEN_DNS, "DNSName" }, + { GEN_DNS, "DNS" }, + { GEN_IPADD, "IPAddress" }, + { GEN_IPADD, "IP" }, + }; + const char *type = luaL_checkstring(L, index); + unsigned i; + + for (i = 0; i < countof(table); i++) { + if (strieq(table[i].name, type)) + return table[i].type; + } + + return luaL_error(L, "%s: invalid type", type), 0; +} /* gn_checktype() */ + + +static int gn_add(lua_State *L) { + GENERAL_NAMES *gens = checksimple(L, 1, X509_GENS_CLASS); + int type = gn_checktype(L, 2); + size_t len; + const char *txt = luaL_checklstring(L, 3, &len); + GENERAL_NAME *gen = NULL; + union { struct in6_addr in6; struct in_addr in; } ip; + + if (type == GEN_IPADD) { + if (strchr(txt, ':')) { + if (1 != inet_pton(AF_INET6, txt, &ip.in6)) + return luaL_error(L, "%s: invalid address", txt); + + txt = (char *)ip.in6.s6_addr; + len = 16; + } else { + if (1 != inet_pton(AF_INET, txt, &ip.in)) + return luaL_error(L, "%s: invalid address", txt); + + txt = (char *)&ip.in.s_addr; + len = 4; + } + } + + if (!(gen = GENERAL_NAME_new())) + goto error; + + gen->type = type; + + if (!(gen->d.ia5 = M_ASN1_IA5STRING_new())) + goto error; + + if (!ASN1_STRING_set(gen->d.ia5, (unsigned char *)txt, len)) + goto error; + + sk_GENERAL_NAME_push(gens, gen); + + lua_pushboolean(L, 1); + + return 1; +error: + GENERAL_NAME_free(gen); + + return throwssl(L, "x509.altname:add"); +} /* gn_add() */ + + +static int gn__next(lua_State *L) { + GENERAL_NAMES *gens = checksimple(L, lua_upvalueindex(1), X509_GENS_CLASS); + int i = lua_tointeger(L, lua_upvalueindex(2)); + int n = sk_GENERAL_NAME_num(gens); + + lua_settop(L, 0); + + while (i < n) { + GENERAL_NAME *name; + const char *tag, *txt; + size_t len; + union { struct in_addr in; struct in6_addr in6; } ip; + char buf[INET6_ADDRSTRLEN + 1]; + int af; + + if (!(name = sk_GENERAL_NAME_value(gens, i++))) + continue; + + switch (name->type) { + case GEN_EMAIL: + tag = "email"; + txt = (char *)M_ASN1_STRING_data(name->d.rfc822Name); + len = M_ASN1_STRING_length(name->d.rfc822Name); + + break; + case GEN_URI: + tag = "URI"; + txt = (char *)M_ASN1_STRING_data(name->d.uniformResourceIdentifier); + len = M_ASN1_STRING_length(name->d.uniformResourceIdentifier); + + break; + case GEN_DNS: + tag = "DNS"; + txt = (char *)M_ASN1_STRING_data(name->d.dNSName); + len = M_ASN1_STRING_length(name->d.dNSName); + + break; + case GEN_IPADD: + tag = "IP"; + txt = (char *)M_ASN1_STRING_data(name->d.iPAddress); + len = M_ASN1_STRING_length(name->d.iPAddress); + + switch (len) { + case 16: + memcpy(ip.in6.s6_addr, txt, 16); + af = AF_INET6; + + break; + case 4: + memcpy(&ip.in.s_addr, txt, 4); + af = AF_INET; + + break; + default: + continue; + } + + if (!(txt = inet_ntop(af, &ip, buf, sizeof buf))) + continue; + + len = strlen(txt); + + break; + default: + continue; + } + + lua_pushstring(L, tag); + lua_pushlstring(L, txt, len); + + break; + } + + lua_pushinteger(L, i); + lua_replace(L, lua_upvalueindex(2)); + + return lua_gettop(L); +} /* gn__next() */ + +static int gn__pairs(lua_State *L) { + lua_settop(L, 1); + lua_pushinteger(L, 0); + lua_pushcclosure(L, &gn__next, 2); + + return 1; +} /* gn__pairs() */ + + +static int gn__gc(lua_State *L) { + GENERAL_NAMES **ud = luaL_checkudata(L, 1, X509_GENS_CLASS); + + sk_GENERAL_NAME_pop_free(*ud, GENERAL_NAME_free); + *ud = NULL; + + return 0; +} /* gn__gc() */ + + +static const luaL_Reg gn_methods[] = { + { "add", &gn_add }, + { NULL, NULL }, +}; + +static const luaL_Reg gn_metatable[] = { + { "__pairs", &gn__pairs }, + { "__gc", &gn__gc }, + { NULL, NULL }, +}; + + +static const luaL_Reg gn_globals[] = { + { "new", &gn_new }, + { "interpose", &gn_interpose }, + { NULL, NULL }, +}; + +int luaopen__openssl_x509_altname(lua_State *L) { + initall(L); + + luaL_newlib(L, gn_globals); + + return 1; +} /* luaopen__openssl_x509_altname() */ + + +/* + * X509 - openssl.x509.cert + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static int xc_new(lua_State *L) { + const char *pem; + size_t len; + X509 **ud; + + lua_settop(L, 1); + + ud = prepsimple(L, X509_CERT_CLASS); + + if ((pem = luaL_optlstring(L, 1, NULL, &len))) { + BIO *tmp; + int ok; + + if (!(tmp = BIO_new_mem_buf((char *)pem, len))) + return throwssl(L, "x509.cert.new"); + + ok = !!PEM_read_bio_X509(tmp, ud, 0, ""); /* no password */ + + BIO_free(tmp); + + if (!ok) + return throwssl(L, "x509.cert.new"); + } else { + if (!(*ud = X509_new())) + return throwssl(L, "x509.cert.new"); + + X509_gmtime_adj(X509_get_notBefore(*ud), 0); + X509_gmtime_adj(X509_get_notAfter(*ud), 0); + } + + return 1; +} /* xc_new() */ + + +static int xc_interpose(lua_State *L) { + return interpose(L, X509_CERT_CLASS); +} /* xc_interpose() */ + + +static int xc_getVersion(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + + lua_pushinteger(L, X509_get_version(crt) + 1); + + return 1; +} /* xc_getVersion() */ + + +static int xc_setVersion(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + int version = luaL_checkint(L, 2); + + if (!X509_set_version(crt, version - 1)) + return luaL_error(L, "x509.cert:setVersion: %d: invalid version", version); + + lua_pushboolean(L, 1); + + return 1; +} /* xc_setVersion() */ + + +static int xc_getSerial(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + BIGNUM *serial = bn_push(L); + ASN1_INTEGER *i; + + if ((i = X509_get_serialNumber(crt))) { + if (!ASN1_INTEGER_to_BN(i, serial)) + return throwssl(L, "x509.cert:getSerial"); + } + + return 1; +} /* xc_getSerial() */ + + +static int xc_setSerial(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + ASN1_INTEGER *serial; + + if (!(serial = BN_to_ASN1_INTEGER(checkbig(L, 2), NULL))) + goto error; + + if (!X509_set_serialNumber(crt, serial)) + goto error; + + ASN1_INTEGER_free(serial); + + lua_pushboolean(L, 1); + + return 1; +error: + ASN1_INTEGER_free(serial); + + return throwssl(L, "x509.cert:setSerial"); +} /* xc_setSerial() */ + + +static int xc_digest(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + const char *type = luaL_optstring(L, 2, "sha1"); + int format = luaL_checkoption(L, 3, "x", (const char *[]){ "s", "x", "n", NULL }); + const EVP_MD *ctx; + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned len; + + lua_settop(L, 3); /* self, type, hex */ + + if (!(ctx = EVP_get_digestbyname(type))) + return luaL_error(L, "x509.cert:digest: %s: invalid digest type", type); + + X509_digest(crt, ctx, md, &len); + + switch (format) { + case 2: { + BIGNUM *bn = bn_push(L); + + if (!BN_bin2bn(md, len, bn)) + return throwssl(L, "x509.cert:digest"); + + break; + } + case 1: { + static const unsigned char x[16] = "0123456789abcdef"; + luaL_Buffer B; + unsigned i; + +#if LUA_VERSION_NUM < 502 + luaL_buffinit(L, &B); +#else + luaL_buffinitsize(L, &B, 2 * len); +#endif + + for (i = 0; i < len; i++) { + luaL_addchar(&B, x[0x0f & (md[i] >> 4)]); + luaL_addchar(&B, x[0x0f & (md[i] >> 0)]); + } + + luaL_pushresult(&B); + + break; + } + default: + lua_pushlstring(L, (const char *)md, len); + + break; + } /* switch() */ + + return 1; +} /* xc_digest() */ + + +static _Bool isleap(int year) { + if (year >= 0) + return !(year % 4) && ((year % 100) || !(year % 400)); + else + return isleap(-(year + 1)); +} /* isleap() */ + + +static int yday(int year, int mon, int mday) { + static const int past[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + int yday = past[CLAMP(mon, 0, 11)] + CLAMP(mday, 1, 31) - 1; + + return yday + (mon > 1 && isleap(year)); +} /* yday() */ + + +static int tm_yday(const struct tm *tm) { + return (tm->tm_yday)? tm->tm_yday : yday(1900 + tm->tm_year, tm->tm_mon, tm->tm_mday); +} /* tm_yday() */ + + +static int leaps(int year) { + if (year >= 0) + return (year / 400) + (year / 4) - (year / 100); + else + return -(leaps(-(year + 1)) + 1); +} /* leaps() */ + + +static double tm2unix(const struct tm *tm, int gmtoff) { + int year = tm->tm_year + 1900; + double ts; + + ts = 86400.0 * 365.0 * (year - 1970); + ts += 86400.0 * (leaps(year - 1) - leaps(1969)); + ts += 86400 * tm_yday(tm); + ts += 3600 * tm->tm_hour; + ts += 60 * tm->tm_min; + ts += CLAMP(tm->tm_sec, 0, 59); + ts += (year < 1970)? gmtoff : -gmtoff; + + return ts; +} /* tm2unix() */ + + +static _Bool scan(int *i, char **cp, int n, int signok) { + int sign = 1; + + *i = 0; + + if (signok) { + if (**cp == '-') { + sign = -1; + ++*cp; + } else if (**cp == '+') { + ++*cp; + } + } + + while (n-- > 0) { + if (**cp < '0' || **cp > '9') + return 0; + + *i *= 10; + *i += *(*cp)++ - '0'; + } + + *i *= sign; + + return 1; +} /* scan() */ + + +static double timeutc(ASN1_TIME *time) { + char buf[32] = "", *cp; + struct tm tm = { 0 }; + int gmtoff = 0, year, i; + double ts; + + if (!ASN1_TIME_check(time)) + return 0; + + cp = strncpy(buf, (const char *)ASN1_STRING_data((ASN1_STRING *)time), sizeof buf - 1); + + if (ASN1_STRING_type(time) == V_ASN1_GENERALIZEDTIME) { + if (!scan(&year, &cp, 4, 1)) + goto badfmt; + } else { + if (!scan(&year, &cp, 2, 0)) + goto badfmt; + year += (year < 50)? 2000 : 1999; + } + + tm.tm_year = year - 1900; + + if (!scan(&i, &cp, 2, 0)) + goto badfmt; + + tm.tm_mon = CLAMP(i, 1, 12) - 1; + + if (!scan(&i, &cp, 2, 0)) + goto badfmt; + + tm.tm_mday = CLAMP(i, 1, 31); + + tm.tm_yday = yday(year, tm.tm_mon, tm.tm_mday); + + if (!scan(&i, &cp, 2, 0)) + goto badfmt; + + tm.tm_hour = CLAMP(i, 0, 23); + + if (!scan(&i, &cp, 2, 0)) + goto badfmt; + + tm.tm_min = CLAMP(i, 0, 59); + + if (*cp >= '0' && *cp <= '9') { + if (!scan(&i, &cp, 2, 0)) + goto badfmt; + + tm.tm_sec = CLAMP(i, 0, 59); + } + + if (*cp == '+' || *cp == '-') { + int sign = (*cp++ == '-')? -1 : 1; + int hh, mm; + + if (!scan(&hh, &cp, 2, 0) || !scan(&mm, &cp, 2, 0)) + goto badfmt; + + gmtoff = (CLAMP(hh, 0, 23) * 3600) + + (CLAMP(mm, 0, 59) * 60); + + gmtoff *= sign; + } + + return tm2unix(&tm, gmtoff); +badfmt: + return INFINITY; +} /* timeutc() */ + + +static int xc_getLifetime(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + double begin = INFINITY, end = INFINITY; + ASN1_TIME *time; + + if ((time = X509_get_notBefore(crt))) + begin = timeutc(time); + + if ((time = X509_get_notAfter(crt))) + end = timeutc(time); + + if (isfinite(begin)) + lua_pushnumber(L, begin); + else + lua_pushnil(L); + + if (isfinite(end)) + lua_pushnumber(L, end); + else + lua_pushnil(L); + + if (isfinite(begin) && isfinite(end) && begin <= end) + lua_pushnumber(L, fabs(end - begin)); + else + lua_pushnumber(L, 0.0); + + return 3; +} /* xc_getLifetime() */ + + +static int xc_setLifetime(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + ASN1_TIME *time; + double ut; + const char *dt; + + lua_settop(L, 3); + + if (lua_isnumber(L, 2)) { + ut = lua_tonumber(L, 2); + + if (!ASN1_TIME_set(X509_get_notBefore(crt), ut)) + return throwssl(L, "x509.cert:setLifetime"); +#if 0 + } else if ((dt = luaL_optstring(L, 2, 0))) { + if (!ASN1_TIME_set_string(X509_get_notBefore(crt), dt)) + return throwssl(L, "x509.cert:setLifetime"); +#endif + } + + if (lua_isnumber(L, 3)) { + ut = lua_tonumber(L, 3); + + if (!ASN1_TIME_set(X509_get_notAfter(crt), ut)) + return throwssl(L, "x509.cert:setLifetime"); +#if 0 + } else if ((dt = luaL_optstring(L, 3, 0))) { + if (!ASN1_TIME_set_string(X509_get_notAfter(crt), dt)) + return throwssl(L, "x509.cert:setLifetime"); +#endif + } + + lua_pushboolean(L, 1); + + return 1; +} /* xc_setLifetime() */ + + +static int xc_getIssuer(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + X509_NAME *name; + + if (!(name = X509_get_issuer_name(crt))) + return 0; + + xn_dup(L, name); + + return 1; +} /* xc_getIssuer() */ + + +static int xc_setIssuer(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + X509_NAME *name = checksimple(L, 2, X509_NAME_CLASS); + + if (!X509_set_issuer_name(crt, name)) + return throwssl(L, "x509.cert:setIssuer"); + + lua_pushboolean(L, 1); + + return 1; +} /* xc_setIssuer() */ + + +static int xc_getSubject(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + X509_NAME *name; + + if (!(name = X509_get_subject_name(crt))) + return 0; + + xn_dup(L, name); + + return 1; +} /* xc_getSubject() */ + + +static int xc_setSubject(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + X509_NAME *name = checksimple(L, 2, X509_NAME_CLASS); + + if (!X509_set_subject_name(crt, name)) + return throwssl(L, "x509.cert:setSubject"); + + lua_pushboolean(L, 1); + + return 1; +} /* xc_setSubject() */ + + +static void xc_setCritical(X509 *crt, int nid, _Bool yes) { + X509_EXTENSION *ext; + int loc; + + if ((loc = X509_get_ext_by_NID(crt, nid, -1)) >= 0 + && (ext = X509_get_ext(crt, loc))) + X509_EXTENSION_set_critical(ext, yes); +} /* xc_setCritical() */ + + +static _Bool xc_getCritical(X509 *crt, int nid) { + X509_EXTENSION *ext; + int loc; + + if ((loc = X509_get_ext_by_NID(crt, nid, -1)) >= 0 + && (ext = X509_get_ext(crt, loc))) + return X509_EXTENSION_get_critical(ext); + else + return 0; +} /* xc_getCritical() */ + + +static int xc_getIssuerAlt(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + GENERAL_NAMES *gens; + + if (!(gens = X509_get_ext_d2i(crt, NID_issuer_alt_name, 0, 0))) + return 0; + + gn_dup(L, gens); + + return 1; +} /* xc_getIssuerAlt() */ + + +static int xc_setIssuerAlt(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + GENERAL_NAMES *gens = checksimple(L, 2, X509_GENS_CLASS); + + if (!X509_add1_ext_i2d(crt, NID_issuer_alt_name, gens, 0, X509V3_ADD_REPLACE)) + return throwssl(L, "x509.altname:setIssuerAlt"); + + lua_pushboolean(L, 1); + + return 1; +} /* xc_setIssuerAlt() */ + + +static int xc_getSubjectAlt(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + GENERAL_NAMES *gens; + + if (!(gens = X509_get_ext_d2i(crt, NID_subject_alt_name, 0, 0))) + return 0; + + gn_dup(L, gens); + + return 1; +} /* xc_getSubjectAlt() */ + + +static int xc_setSubjectAlt(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + GENERAL_NAMES *gens = checksimple(L, 2, X509_GENS_CLASS); + + if (!X509_add1_ext_i2d(crt, NID_subject_alt_name, gens, 0, X509V3_ADD_REPLACE)) + return throwssl(L, "x509.altname:setSubjectAlt"); + + lua_pushboolean(L, 1); + + return 1; +} /* xc_setSubjectAlt() */ + + +static int xc_getIssuerAltCritical(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + + lua_pushboolean(L, xc_getCritical(crt, NID_issuer_alt_name)); + + return 1; +} /* xc_getIssuerAltCritical() */ + + +static int xc_setIssuerAltCritical(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + + luaL_checkany(L, 2); + xc_setCritical(crt, NID_issuer_alt_name, lua_toboolean(L, 2)); + + lua_pushboolean(L, 1); + + return 1; +} /* xc_setIssuerAltCritical() */ + + +static int xc_getSubjectAltCritical(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + + lua_pushboolean(L, xc_getCritical(crt, NID_subject_alt_name)); + + return 1; +} /* xc_getSubjectAltCritical() */ + + +static int xc_setSubjectAltCritical(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + + luaL_checkany(L, 2); + xc_setCritical(crt, NID_subject_alt_name, lua_toboolean(L, 2)); + + lua_pushboolean(L, 1); + + return 1; +} /* xc_setSubjectAltCritical() */ + + +static int xc_getBasicConstraint(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + BASIC_CONSTRAINTS *bs; + int CA, pathLen; + + if (!(bs = X509_get_ext_d2i(crt, NID_basic_constraints, 0, 0))) { + /* FIXME: detect error or just non-existent */ + + if (lua_gettop(L) > 1) + return 0; + + lua_newtable(L); + + return 1; + } + + CA = bs->ca; + pathLen = ASN1_INTEGER_get(bs->pathlen); + + BASIC_CONSTRAINTS_free(bs); + + if (lua_gettop(L) > 1) { + int n = 0, i, top; + + for (i = 2, top = lua_gettop(L); i <= top; i++) { + switch (checkoption(L, i, 0, (const char *[]){ "CA", "pathLen", "pathLenConstraint", NULL })) { + case 0: + lua_pushboolean(L, CA); + n++; + break; + case 1: + /* FALL THROUGH */ + case 2: + lua_pushinteger(L, pathLen); + n++; + break; + } + } + + return n; + } else { + lua_newtable(L); + + lua_pushboolean(L, CA); + lua_setfield(L, -2, "CA"); + + lua_pushinteger(L, pathLen); + lua_setfield(L, -2, "pathLen"); + + return 1; + } +} /* xc_getBasicConstraint() */ + + +static int xc_setBasicConstraint(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + BASIC_CONSTRAINTS *bs = 0; + int CA = -1, pathLen = -1; + int critical = 0; + + luaL_checkany(L, 2); + + if (lua_istable(L, 2)) { + lua_getfield(L, 2, "CA"); + if (!lua_isnil(L, -1)) + CA = lua_toboolean(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 2, "pathLen"); + pathLen = luaL_optint(L, -1, pathLen); + lua_pop(L, 1); + + lua_getfield(L, 2, "pathLenConstraint"); + pathLen = luaL_optint(L, -1, pathLen); + lua_pop(L, 1); + + if (!(bs = BASIC_CONSTRAINTS_new())) + goto error; + } else { + lua_settop(L, 3); + + switch (checkoption(L, 2, 0, (const char *[]){ "CA", "pathLen", "pathLenConstraint", NULL })) { + case 0: + luaL_checktype(L, 3, LUA_TBOOLEAN); + CA = lua_toboolean(L, 3); + + break; + case 1: + /* FALL THROUGH */ + case 2: + pathLen = luaL_checkint(L, 3); + + break; + } + + if (!(bs = X509_get_ext_d2i(crt, NID_basic_constraints, &critical, 0))) { + /* FIXME: detect whether error or just non-existent */ + if (!(bs = BASIC_CONSTRAINTS_new())) + goto error; + } + } + + if (CA != -1) + bs->ca = CA; + + if (pathLen >= 0) { + ASN1_INTEGER_free(bs->pathlen); + + if (!(bs->pathlen = M_ASN1_INTEGER_new())) + goto error; + + if (!ASN1_INTEGER_set(bs->pathlen, pathLen)) + goto error; + } + + if (!X509_add1_ext_i2d(crt, NID_basic_constraints, bs, critical, X509V3_ADD_REPLACE)) + goto error; + + BASIC_CONSTRAINTS_free(bs); + + lua_pushboolean(L, 1); + + return 1; +error: + BASIC_CONSTRAINTS_free(bs); + + return throwssl(L, "x509.cert:setBasicConstraint"); +} /* xc_setBasicConstraint() */ + + +static int xc_getBasicConstraintsCritical(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + + lua_pushboolean(L, xc_getCritical(crt, NID_basic_constraints)); + + return 1; +} /* xc_getBasicConstraintsCritical() */ + + +static int xc_setBasicConstraintsCritical(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + + luaL_checkany(L, 2); + xc_setCritical(crt, NID_basic_constraints, lua_toboolean(L, 2)); + + lua_pushboolean(L, 1); + + return 1; +} /* xc_setBasicConstraintsCritical() */ + + +static int xc_isIssuedBy(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + X509 *issuer = checksimple(L, 2, X509_CERT_CLASS); + EVP_PKEY *key; + int ok, why = 0; + + ERR_clear_error(); + + if (X509_V_OK != (why = X509_check_issued(issuer, crt))) + goto done; + + if (!(key = X509_get_pubkey(issuer))) { + why = X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY; + goto done; + } + + ok = (1 == X509_verify(crt, key)); + + EVP_PKEY_free(key); + + if (!ok) + why = X509_V_ERR_CERT_SIGNATURE_FAILURE; + +done: + if (why != X509_V_OK) { + lua_pushboolean(L, 0); + lua_pushstring(L, X509_verify_cert_error_string(why)); + + return 2; + } else { + lua_pushboolean(L, 1); + + return 1; + } +} /* xc_isIssuedBy() */ + + +static int xc_getPublicKey(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + EVP_PKEY **key = prepsimple(L, PUBKEY_CLASS); + + if (!(*key = X509_get_pubkey(crt))) + return throwssl(L, "x509.cert:getPublicKey"); + + return 1; +} /* xc_getPublicKey() */ + + +static int xc_setPublicKey(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + EVP_PKEY *key = checksimple(L, 2, PUBKEY_CLASS); + + if (!X509_set_pubkey(crt, key)) + return throwssl(L, "x509.cert:setPublicKey"); + + lua_pushboolean(L, 1); + + return 1; +} /* xc_setPublicKey() */ + + +static const EVP_MD *xc_signature(lua_State *L, int index, EVP_PKEY *key) { + const char *id; + const EVP_MD *md; + + if ((id = luaL_optstring(L, index, NULL))) + return ((md = EVP_get_digestbyname(id)))? md : EVP_md_null(); + + switch (EVP_PKEY_type(key->type)) { + case EVP_PKEY_RSA: + return EVP_sha1(); + case EVP_PKEY_DSA: + return EVP_dss1(); + case EVP_PKEY_EC: + return EVP_ecdsa(); + default: + return EVP_md_null(); + } +} /* xc_signature() */ + +static int xc_sign(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + EVP_PKEY *key = checksimple(L, 2, PUBKEY_CLASS); + + if (!X509_sign(crt, key, xc_signature(L, 3, key))) + return throwssl(L, "x509.cert:sign"); + + lua_pushboolean(L, 1); + + return 1; +} /* xc_sign() */ + + +static int xc__tostring(lua_State *L) { + X509 *crt = checksimple(L, 1, X509_CERT_CLASS); + int fmt = checkoption(L, 2, "pem", (const char *[]){ "pem", NULL }); + BIO *bio = getbio(L); + char *pem; + long len; + + if (!PEM_write_bio_X509(bio, crt)) + return throwssl(L, "x509.cert:__tostring"); + + len = BIO_get_mem_data(bio, &pem); + + lua_pushlstring(L, pem, len); + + return 1; +} /* xc__tostring() */ + + +static int xc__gc(lua_State *L) { + X509 **ud = luaL_checkudata(L, 1, X509_CERT_CLASS); + + X509_free(*ud); + *ud = NULL; + + return 0; +} /* xc__gc() */ + + +static const luaL_Reg xc_methods[] = { + { "getVersion", &xc_getVersion }, + { "setVersion", &xc_setVersion }, + { "getSerial", &xc_getSerial }, + { "setSerial", &xc_setSerial }, + { "digest", &xc_digest }, + { "getLifetime", &xc_getLifetime }, + { "setLifetime", &xc_setLifetime }, + { "getIssuer", &xc_getIssuer }, + { "setIssuer", &xc_setIssuer }, + { "getSubject", &xc_getSubject }, + { "setSubject", &xc_setSubject }, + { "getIssuerAlt", &xc_getIssuerAlt }, + { "setIssuerAlt", &xc_setIssuerAlt }, + { "getSubjectAlt", &xc_getSubjectAlt }, + { "setSubjectAlt", &xc_setSubjectAlt }, + { "getIssuerAltCritical", &xc_getIssuerAltCritical }, + { "setIssuerAltCritical", &xc_setIssuerAltCritical }, + { "getSubjectAltCritical", &xc_getSubjectAltCritical }, + { "setSubjectAltCritical", &xc_setSubjectAltCritical }, + { "getBasicConstraints", &xc_getBasicConstraint }, + { "getBasicConstraint", &xc_getBasicConstraint }, + { "setBasicConstraints", &xc_setBasicConstraint }, + { "setBasicConstraint", &xc_setBasicConstraint }, + { "getBasicConstraintsCritical", &xc_getBasicConstraintsCritical }, + { "setBasicConstraintsCritical", &xc_setBasicConstraintsCritical }, + { "isIssuedBy", &xc_isIssuedBy }, + { "getPublicKey", &xc_getPublicKey }, + { "setPublicKey", &xc_setPublicKey }, + { "sign", &xc_sign }, + { NULL, NULL }, +}; + +static const luaL_Reg xc_metatable[] = { + { "__tostring", &xc__tostring }, + { "__gc", &xc__gc }, + { NULL, NULL }, +}; + + +static const luaL_Reg xc_globals[] = { + { "new", &xc_new }, + { "interpose", &xc_interpose }, + { NULL, NULL }, +}; + +int luaopen__openssl_x509_cert(lua_State *L) { + initall(L); + + luaL_newlib(L, xc_globals); + + return 1; +} /* luaopen__openssl_x509_cert() */ + + +/* + * X509_REQ - openssl.x509.csr + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static int xr_new(lua_State *L) { + const char *pem; + size_t len; + X509_REQ **ud; + X509 *crt; + + lua_settop(L, 1); + + ud = prepsimple(L, X509_CSR_CLASS); + + if ((crt = testsimple(L, 1, X509_CERT_CLASS))) { + if (!(*ud = X509_to_X509_REQ(crt, 0, 0))) + return throwssl(L, "x509.csr.new"); + } else if ((pem = luaL_optlstring(L, 1, NULL, &len))) { + BIO *tmp; + int ok; + + if (!(tmp = BIO_new_mem_buf((char *)pem, len))) + return throwssl(L, "x509.csr.new"); + + ok = !!PEM_read_bio_X509_REQ(tmp, ud, 0, ""); /* no password */ + + BIO_free(tmp); + + if (!ok) + return throwssl(L, "x509.csr.new"); + } else { + if (!(*ud = X509_REQ_new())) + return throwssl(L, "x509.csr.new"); + } + + return 1; +} /* xr_new() */ + + +static int xr_interpose(lua_State *L) { + return interpose(L, X509_CSR_CLASS); +} /* xr_interpose() */ + + +static int xr_getVersion(lua_State *L) { + X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS); + + lua_pushinteger(L, X509_REQ_get_version(csr) + 1); + + return 1; +} /* xr_getVersion() */ + + +static int xr_setVersion(lua_State *L) { + X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS); + int version = luaL_checkint(L, 2); + + if (!X509_REQ_set_version(csr, version - 1)) + return luaL_error(L, "x509.csr:setVersion: %d: invalid version", version); + + lua_pushboolean(L, 1); + + return 1; +} /* xr_setVersion() */ + + +static int xr_getSubject(lua_State *L) { + X509_REQ *crt = checksimple(L, 1, X509_CSR_CLASS); + X509_NAME *name; + + if (!(name = X509_REQ_get_subject_name(crt))) + return 0; + + xn_dup(L, name); + + return 1; +} /* xr_getSubject() */ + + +static int xr_setSubject(lua_State *L) { + X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS); + X509_NAME *name = checksimple(L, 2, X509_NAME_CLASS); + + if (!X509_REQ_set_subject_name(csr, name)) + return throwssl(L, "x509.csr:setSubject"); + + lua_pushboolean(L, 1); + + return 1; +} /* xr_setSubject() */ + + +static int xr_getPublicKey(lua_State *L) { + X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS); + EVP_PKEY **key = prepsimple(L, PUBKEY_CLASS); + + if (!(*key = X509_REQ_get_pubkey(csr))) + return throwssl(L, "x509.cert:getPublicKey"); + + return 1; +} /* xr_getPublicKey() */ + + +static int xr_setPublicKey(lua_State *L) { + X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS); + EVP_PKEY *key = checksimple(L, 2, PUBKEY_CLASS); + + if (!X509_REQ_set_pubkey(csr, key)) + return throwssl(L, "x509.csr:setPublicKey"); + + lua_pushboolean(L, 1); + + return 1; +} /* xr_setPublicKey() */ + + +static int xr_sign(lua_State *L) { + X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS); + EVP_PKEY *key = checksimple(L, 2, PUBKEY_CLASS); + + if (!X509_REQ_sign(csr, key, xc_signature(L, 3, key))) + return throwssl(L, "x509.csr:sign"); + + lua_pushboolean(L, 1); + + return 1; +} /* xr_sign() */ + + +static int xr__tostring(lua_State *L) { + X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS); + int fmt = checkoption(L, 2, "pem", (const char *[]){ "pem", NULL }); + BIO *bio = getbio(L); + char *pem; + long len; + + if (!PEM_write_bio_X509_REQ(bio, csr)) + return throwssl(L, "x509.csr:__tostring"); + + len = BIO_get_mem_data(bio, &pem); + + lua_pushlstring(L, pem, len); + + return 1; +} /* xr__tostring() */ + + +static int xr__gc(lua_State *L) { + X509_REQ **ud = luaL_checkudata(L, 1, X509_CSR_CLASS); + + X509_REQ_free(*ud); + *ud = NULL; + + return 0; +} /* xr__gc() */ + +static const luaL_Reg xr_methods[] = { + { "getVersion", &xr_getVersion }, + { "setVersion", &xr_setVersion }, + { "getSubject", &xr_getSubject }, + { "setSubject", &xr_setSubject }, + { "getPublicKey", &xr_getPublicKey }, + { "setPublicKey", &xr_setPublicKey }, + { "sign", &xr_sign }, + { NULL, NULL }, +}; + +static const luaL_Reg xr_metatable[] = { + { "__tostring", &xr__tostring }, + { "__gc", &xr__gc }, + { NULL, NULL }, +}; + + +static const luaL_Reg xr_globals[] = { + { "new", &xr_new }, + { "interpose", &xr_interpose }, + { NULL, NULL }, +}; + +int luaopen__openssl_x509_csr(lua_State *L) { + initall(L); + + luaL_newlib(L, xr_globals); + + return 1; +} /* luaopen__openssl_x509_csr() */ + + +/* + * STACK_OF(X509) - openssl.x509.chain + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static void xl_dup(lua_State *L, STACK_OF(X509) *src, _Bool copy) { + STACK_OF(X509) **dst = prepsimple(L, X509_CHAIN_CLASS); + X509 *crt; + int i, n; + + if (copy) { + if (!(*dst = sk_X509_new_null())) + goto error; + + n = sk_X509_num(src); + + for (i = 0; i < n; i++) { + if (!(crt = sk_X509_value(src, i))) + continue; + + if (!(crt = X509_dup(crt))) + goto error; + + if (!sk_X509_push(*dst, crt)) { + X509_free(crt); + goto error; + } + } + } else { + if (!(*dst = sk_X509_dup(src))) + goto error; + + n = sk_X509_num(*dst); + + for (i = 0; i < n; i++) { + if (!(crt = sk_X509_value(*dst, i))) + continue; + CRYPTO_add(&crt->references, 1, CRYPTO_LOCK_X509); + } + } + + return; +error: + throwssl(L, "sk_X509_dup"); +} /* xl_dup() */ + + +static int xl_new(lua_State *L) { + STACK_OF(X509) **chain = prepsimple(L, X509_CHAIN_CLASS); + + if (!(*chain = sk_X509_new_null())) + return throwssl(L, "x509.chain.new"); + + return 1; +} /* xl_new() */ + + +static int xl_interpose(lua_State *L) { + return interpose(L, X509_CHAIN_CLASS); +} /* xl_interpose() */ + + +static int xl_add(lua_State *L) { + STACK_OF(X509) *chain = checksimple(L, 1, X509_CHAIN_CLASS); + X509 *crt = checksimple(L, 2, X509_CERT_CLASS); + X509 *dup; + + if (!(dup = X509_dup(crt))) + return throwssl(L, "x509.chain:add"); + + if (!sk_X509_push(chain, dup)) { + X509_free(dup); + return throwssl(L, "x509.chain:add"); + } + + lua_pushboolean(L, 1); + + return 1; +} /* xl_add() */ + + +static int xl__next(lua_State *L) { + STACK_OF(X509) *chain = checksimple(L, lua_upvalueindex(1), X509_CHAIN_CLASS); + int i = lua_tointeger(L, lua_upvalueindex(2)); + int n = sk_X509_num(chain); + + lua_settop(L, 0); + + while (i < n) { + X509 *crt, **ret; + + if (!(crt = sk_X509_value(chain, i++))) + continue; + + lua_pushinteger(L, i); + + ret = prepsimple(L, X509_CERT_CLASS); + + if (!(*ret = X509_dup(crt))) + return throwssl(L, "x509.chain:__next"); + + break; + } + + lua_pushinteger(L, i); + lua_replace(L, lua_upvalueindex(2)); + + return lua_gettop(L); +} /* xl__next() */ + +static int xl__pairs(lua_State *L) { + lua_settop(L, 1); + lua_pushinteger(L, 0); + lua_pushcclosure(L, &xl__next, 2); + + return 1; +} /* xl__pairs() */ + + +static int xl__gc(lua_State *L) { + STACK_OF(X509) **chain = luaL_checkudata(L, 1, X509_CHAIN_CLASS); + + sk_X509_pop_free(*chain, X509_free); + *chain = NULL; + + return 0; +} /* xl__gc() */ + + +static const luaL_Reg xl_methods[] = { + { "add", &xl_add }, + { NULL, NULL }, +}; + +static const luaL_Reg xl_metatable[] = { + { "__pairs", &xl__pairs }, + { "__ipairs", &xl__pairs }, + { "__gc", &xl__gc }, + { NULL, NULL }, +}; + +static const luaL_Reg xl_globals[] = { + { "new", &xl_new }, + { "interpose", &xl_interpose }, + { NULL, NULL }, +}; + +int luaopen__openssl_x509_chain(lua_State *L) { + initall(L); + + luaL_newlib(L, xl_globals); + + return 1; +} /* luaopen__openssl_x509_chain() */ + + +/* + * X509_STORE - openssl.x509.store + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static int xs_new(lua_State *L) { + X509_STORE **ud = prepsimple(L, X509_STORE_CLASS); + + if (!(*ud = X509_STORE_new())) + return throwssl(L, "x509.store"); + + return 1; +} /* xs_new() */ + + +static int xs_interpose(lua_State *L) { + return interpose(L, X509_STORE_CLASS); +} /* xs_interpose() */ + + +static int xs_add(lua_State *L) { + X509_STORE *store = checksimple(L, 1, X509_STORE_CLASS); + int i, top = lua_gettop(L); + + for (i = 2; i <= top; i++) { + if (lua_isuserdata(L, i)) { + X509 *crt = checksimple(L, i, X509_CERT_CLASS); + X509 *dup; + + if (!(dup = X509_dup(crt))) + return throwssl(L, "x509.store:add"); + + if (!X509_STORE_add_cert(store, dup)) { + X509_free(dup); + return throwssl(L, "x509.store:add"); + } + } else { + const char *path = luaL_checkstring(L, i); + struct stat st; + int ok; + + if (0 != stat(path, &st)) + return luaL_error(L, "%s: %s", path, strerror(errno)); + + if (S_ISDIR(st.st_mode)) + ok = X509_STORE_load_locations(store, NULL, path); + else + ok = X509_STORE_load_locations(store, path, NULL); + + if (!ok) + return throwssl(L, "x509.store:add"); + } + } + + lua_pushboolean(L, 1); + + return 1; +} /* xs_add() */ + + +static int xs_verify(lua_State *L) { + X509_STORE *store = checksimple(L, 1, X509_STORE_CLASS); + X509 *crt = checksimple(L, 2, X509_CERT_CLASS); + STACK_OF(X509) *chain = NULL, **proof; + X509_STORE_CTX ctx; + int ok, why; + + /* pre-allocate space for a successful return */ + lua_settop(L, 3); + proof = prepsimple(L, X509_CHAIN_CLASS); + + if (!lua_isnoneornil(L, 3)) { + X509 *elm; + int i, n; + + if (!(chain = sk_X509_dup(checksimple(L, 3, X509_CHAIN_CLASS)))) + return throwssl(L, "x509.store:verify"); + + n = sk_X509_num(chain); + + for (i = 0; i < n; i++) { + if (!(elm = sk_X509_value(chain, i))) + continue; + CRYPTO_add(&elm->references, 1, CRYPTO_LOCK_X509); + } + } + + if (!X509_STORE_CTX_init(&ctx, store, crt, chain)) { + sk_X509_pop_free(chain, X509_free); + return throwssl(L, "x509.store:verify"); + } + + ERR_clear_error(); + + ok = X509_verify_cert(&ctx); + + switch (ok) { + case 1: /* verified */ + *proof = X509_STORE_CTX_get1_chain(&ctx); + + X509_STORE_CTX_cleanup(&ctx); + + if (!*proof) + return throwssl(L, "x509.store:verify"); + + lua_pushboolean(L, 1); + lua_pushvalue(L, -2); + + return 2; + case 0: /* not verified */ + why = X509_STORE_CTX_get_error(&ctx); + + X509_STORE_CTX_cleanup(&ctx); + + lua_pushboolean(L, 0); + lua_pushstring(L, X509_verify_cert_error_string(why)); + + return 2; + default: + X509_STORE_CTX_cleanup(&ctx); + + return throwssl(L, "x509.store:verify"); + } +} /* xs_verify() */ + + +static int xs__gc(lua_State *L) { + X509_STORE **ud = luaL_checkudata(L, 1, X509_STORE_CLASS); + + X509_STORE_free(*ud); + *ud = NULL; + + return 0; +} /* xs__gc() */ + + +static const luaL_Reg xs_methods[] = { + { "add", &xs_add }, + { "verify", &xs_verify }, + { NULL, NULL }, +}; + +static const luaL_Reg xs_metatable[] = { + { "__gc", &xs__gc }, + { NULL, NULL }, +}; + +static const luaL_Reg xs_globals[] = { + { "new", &xs_new }, + { "interpose", &xs_interpose }, + { NULL, NULL }, +}; + +int luaopen__openssl_x509_store(lua_State *L) { + initall(L); + + luaL_newlib(L, xs_globals); + + return 1; +} /* luaopen__openssl_x509_store() */ + + +/* + * X509_STORE_CTX - openssl.x509.store.context + * + * This object is intended to be a temporary container in OpenSSL, so the + * memory management is quite clumsy. In particular, it doesn't take + * ownership of the X509_STORE object, which means the reference must be + * held externally for the life of the X509_STORE_CTX object. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#if 0 +static int stx_new(lua_State *L) { + X509_STORE_CTX **ud = prepsimple(L, X509_STCTX_CLASS); + STACK_OF(X509) *chain; + + if (!(*ud = X509_STORE_CTX_new())) + return throwssl(L, "x509.store.context"); + + return 1; +} /* stx_new() */ + + +static int stx_interpose(lua_State *L) { + return interpose(L, X509_STCTX_CLASS); +} /* stx_interpose() */ + + +static int stx_add(lua_State *L) { + X509_STORE_CTX *ctx = checksimple(L, 1, X509_STCTX_CLASS); + + return 0; +} /* stx_add() */ + + +static int stx__gc(lua_State *L) { + X509_STORE **ud = luaL_checkudata(L, 1, X509_STORE_CLASS); + + X509_STORE_free(*ud); + *ud = NULL; + + return 0; +} /* stx__gc() */ + + +static const luaL_Reg stx_methods[] = { + { "add", &stx_add }, + { NULL, NULL }, +}; + +static const luaL_Reg stx_metatable[] = { + { "__gc", &stx__gc }, + { NULL, NULL }, +}; + +static const luaL_Reg stx_globals[] = { + { "new", &stx_new }, + { "interpose", &stx_interpose }, + { NULL, NULL }, +}; + +int luaopen__openssl_x509_store_context(lua_State *L) { + initall(L); + + luaL_newlib(L, stx_globals); + + return 1; +} /* luaopen__openssl_x509_store_context() */ +#endif + + +/* + * SSL_CTX - openssl.ssl.context + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static int sx_new(lua_State *L) { + static const char *opts[] = { + "SSLv2", "SSLv3", "SSLv23", "SSL", "TLSv1", "TLS", NULL + }; + /* later versions of SSL declare a const qualifier on the return type */ + __typeof__(&TLSv1_client_method) method = &TLSv1_client_method; + _Bool srv; + SSL_CTX **ud; + + lua_settop(L, 2); + srv = lua_toboolean(L, 2); + + switch (checkoption(L, 1, "TLS", opts)) { +#ifndef OPENSSL_NO_SSL2 + case 0: /* SSLv2 */ + method = (srv)? &SSLv2_server_method : &SSLv2_client_method; + break; +#endif + case 1: /* SSLv3 */ + method = (srv)? &SSLv3_server_method : &SSLv3_client_method; + break; + case 2: /* SSLv23 */ + /* FALL THROUGH */ + case 3: /* SSL */ + method = (srv)? &SSLv23_server_method : &SSLv23_client_method; + break; + case 4: /* TLSv1 */ + /* FALL THROUGH */ + case 5: /* TLS */ + method = (srv)? &TLSv1_server_method : &TLSv1_client_method; + break; + } + + ud = prepsimple(L, SSL_CTX_CLASS); + + if (!(*ud = SSL_CTX_new(method()))) + return throwssl(L, "ssl.context.new"); + + return 1; +} /* sx_new() */ + + +static int sx_interpose(lua_State *L) { + return interpose(L, SSL_CTX_CLASS); +} /* sx_interpose() */ + + +static int sx_setStore(lua_State *L) { + SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS); + X509_STORE *store = checksimple(L, 2, X509_STORE_CLASS); + + SSL_CTX_set_cert_store(ctx, store); + CRYPTO_add(&store->references, 1, CRYPTO_LOCK_X509_STORE); + + lua_pushboolean(L, 1); + + return 1; +} /* sx_setStore() */ + + +static int sx_setVerify(lua_State *L) { + SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS); + int mode = luaL_optint(L, 2, -1); + int depth = luaL_optint(L, 3, -1); + + if (mode != -1) + SSL_CTX_set_verify(ctx, mode, 0); + + if (depth != -1) + SSL_CTX_set_verify_depth(ctx, depth); + + lua_pushboolean(L, 1); + + return 1; +} /* sx_setVerify() */ + + +static int sx_getVerify(lua_State *L) { + SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS); + + lua_pushinteger(L, SSL_CTX_get_verify_mode(ctx)); + lua_pushinteger(L, SSL_CTX_get_verify_depth(ctx)); + + return 2; +} /* sx_getVerify() */ + + +static int sx_setCertificate(lua_State *L) { + SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS); + X509 *crt = X509_dup(checksimple(L, 2, X509_CERT_CLASS)); + int ok; + + ok = SSL_CTX_use_certificate(ctx, crt); + X509_free(crt); + + if (!ok) + return throwssl(L, "ssl.context:setCertificate"); + + lua_pushboolean(L, 1); + + return 1; +} /* sx_setCertificate() */ + + +static int sx_setPrivateKey(lua_State *L) { + SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS); + EVP_PKEY *key = checksimple(L, 2, PUBKEY_CLASS); + + /* + * NOTE: No easy way to dup the key, but a shared reference should + * be okay as keys are less mutable than certificates. + */ + if (!SSL_CTX_use_PrivateKey(ctx, key)) + return throwssl(L, "ssl.context:setPrivateKey"); + + lua_pushboolean(L, 1); + + return 1; +} /* sx_setPrivateKey() */ + + +static int sx_setCipherList(lua_State *L) { + SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS); + const char *ciphers = luaL_checkstring(L, 2); + + if (!SSL_CTX_set_cipher_list(ctx, ciphers)) + return throwssl(L, "ssl.context:setCipherList"); + + lua_pushboolean(L, 1); + + return 1; +} /* sx_setCipherList() */ + + +static int sx__gc(lua_State *L) { + SSL_CTX **ud = luaL_checkudata(L, 1, SSL_CTX_CLASS); + + SSL_CTX_free(*ud); + *ud = NULL; + + return 0; +} /* sx__gc() */ + + +static const luaL_Reg sx_methods[] = { + { "setStore", &sx_setStore }, + { "setVerify", &sx_setVerify }, + { "getVerify", &sx_getVerify }, + { "setCertificate", &sx_setCertificate }, + { "setPrivateKey", &sx_setPrivateKey }, + { "setCipherList", &sx_setCipherList }, + { NULL, NULL }, +}; + +static const luaL_Reg sx_metatable[] = { + { "__gc", &sx__gc }, + { NULL, NULL }, +}; + +static const luaL_Reg sx_globals[] = { + { "new", &sx_new }, + { "interpose", &sx_interpose }, + { NULL, NULL }, +}; + +int luaopen__openssl_ssl_context(lua_State *L) { + initall(L); + + luaL_newlib(L, sx_globals); + + lua_pushinteger(L, SSL_VERIFY_NONE); + lua_setfield(L, -2, "VERIFY_NONE"); + + lua_pushinteger(L, SSL_VERIFY_PEER); + lua_setfield(L, -2, "VERIFY_PEER"); + + lua_pushinteger(L, SSL_VERIFY_FAIL_IF_NO_PEER_CERT); + lua_setfield(L, -2, "VERIFY_FAIL_IF_NO_PEER_CERT"); + + lua_pushinteger(L, SSL_VERIFY_CLIENT_ONCE); + lua_setfield(L, -2, "VERIFY_CLIENT_ONCE"); + + return 1; +} /* luaopen__openssl_ssl_context() */ + + +/* + * SSL - openssl.ssl + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static int ssl_new(lua_State *L) { + lua_pushnil(L); + + return 1; +} /* ssl_new() */ + + +static int ssl_interpose(lua_State *L) { + return interpose(L, SSL_CLASS); +} /* ssl_interpose() */ + + +static int ssl_getPeerCertificate(lua_State *L) { + SSL *ssl = checksimple(L, 1, SSL_CLASS); + X509 **x509 = prepsimple(L, X509_CERT_CLASS); + + if (!(*x509 = SSL_get_peer_certificate(ssl))) + return 0; + + return 1; +} /* ssl_getPeerCertificate() */ + + +static int ssl_getPeerChain(lua_State *L) { + SSL *ssl = checksimple(L, 1, SSL_CLASS); + STACK_OF(X509) *chain; + + if (!(chain = SSL_get_peer_cert_chain(ssl))) + return 0; + + xl_dup(L, chain, 0); + + return 1; +} /* ssl_getPeerChain() */ + + +static int ssl_getCipherInfo(lua_State *L) { + SSL *ssl = checksimple(L, 1, SSL_CLASS); + const SSL_CIPHER *cipher; + char descr[256]; + + if (!(cipher = SSL_get_current_cipher(ssl))) + return 0; + + lua_newtable(L); + + lua_pushstring(L, SSL_CIPHER_get_name(cipher)); + lua_setfield(L, -2, "name"); + + lua_pushinteger(L, SSL_CIPHER_get_bits(cipher, 0)); + lua_setfield(L, -2, "bits"); + + lua_pushstring(L, SSL_CIPHER_get_version(cipher)); + lua_setfield(L, -2, "version"); + + lua_pushstring(L, SSL_CIPHER_description(cipher, descr, sizeof descr)); + lua_setfield(L, -2, "description"); + + return 1; +} /* ssl_getCipherInfo() */ + + +static int ssl__gc(lua_State *L) { + SSL **ud = luaL_checkudata(L, 1, SSL_CLASS); + + SSL_free(*ud); + *ud = NULL; + + return 0; +} /* ssl__gc() */ + + +static const luaL_Reg ssl_methods[] = { + { "getPeerCertificate", &ssl_getPeerCertificate }, + { "getPeerChain", &ssl_getPeerChain }, + { "getCipherInfo", &ssl_getCipherInfo }, + { NULL, NULL }, +}; + +static const luaL_Reg ssl_metatable[] = { + { "__gc", &ssl__gc }, + { NULL, NULL }, +}; + +static const luaL_Reg ssl_globals[] = { + { "new", &ssl_new }, + { "interpose", &ssl_interpose }, + { NULL, NULL }, +}; + +int luaopen__openssl_ssl(lua_State *L) { + initall(L); + + luaL_newlib(L, ssl_globals); + + return 1; +} /* luaopen__openssl_ssl() */ + + +/* + * Digest - openssl.digest + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static const EVP_MD *md_optdigest(lua_State *L, int index) { + const char *name = luaL_optstring(L, index, "sha1"); + const EVP_MD *type; + + if (!(type = EVP_get_digestbyname(name))) + luaL_argerror(L, index, lua_pushfstring(L, "%s: invalid digest type", name)); + + return type; +} /* md_optdigest() */ + + +static int md_new(lua_State *L) { + const EVP_MD *type = md_optdigest(L, 1); + EVP_MD_CTX *ctx; + + ctx = prepudata(L, sizeof *ctx, DIGEST_CLASS, NULL); + + EVP_MD_CTX_init(ctx); + + if (!EVP_DigestInit_ex(ctx, type, NULL)) + return throwssl(L, "digest.new"); + + return 1; +} /* md_new() */ + + +static int md_interpose(lua_State *L) { + return interpose(L, DIGEST_CLASS); +} /* md_interpose() */ + + +static void md_update_(lua_State *L, EVP_MD_CTX *ctx, int from, int to) { + int i; + + for (i = from; i <= to; i++) { + const void *p; + size_t n; + + p = luaL_checklstring(L, i, &n); + + if (!EVP_DigestUpdate(ctx, p, n)) + throwssl(L, "digest:update"); + } +} /* md_update_() */ + + +static int md_update(lua_State *L) { + EVP_MD_CTX *ctx = luaL_checkudata(L, 1, DIGEST_CLASS); + int i, top = lua_gettop(L); + + md_update_(L, ctx, 2, lua_gettop(L)); + + lua_pushboolean(L, 1); + + return 1; +} /* md_update() */ + + +static int md_final(lua_State *L) { + EVP_MD_CTX *ctx = luaL_checkudata(L, 1, DIGEST_CLASS); + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned len; + + md_update_(L, ctx, 2, lua_gettop(L)); + + if (!EVP_DigestFinal_ex(ctx, md, &len)) + return throwssl(L, "digest:final"); + + lua_pushlstring(L, (char *)md, len); + + return 1; +} /* md_final() */ + + +static int md__gc(lua_State *L) { + EVP_MD_CTX *ctx = luaL_checkudata(L, 1, DIGEST_CLASS); + + EVP_MD_CTX_cleanup(ctx); + + return 0; +} /* md__gc() */ + + +static const luaL_Reg md_methods[] = { + { "update", &md_update }, + { "final", &md_final }, + { NULL, NULL }, +}; + +static const luaL_Reg md_metatable[] = { + { "__gc", &md__gc }, + { NULL, NULL }, +}; + +static const luaL_Reg md_globals[] = { + { "new", &md_new }, + { "interpose", &md_interpose }, + { NULL, NULL }, +}; + +int luaopen__openssl_digest(lua_State *L) { + initall(L); + + luaL_newlib(L, md_globals); + + return 1; +} /* luaopen__openssl_digest() */ + + +/* + * HMAC - openssl.hmac + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static int hmac_new(lua_State *L) { + const void *key; + size_t len; + const EVP_MD *type; + HMAC_CTX *ctx; + + key = luaL_checklstring(L, 1, &len); + type = md_optdigest(L, 2); + + ctx = prepudata(L, sizeof *ctx, HMAC_CLASS, NULL); + + HMAC_Init_ex(ctx, key, len, type, NULL); + + return 1; +} /* hmac_new() */ + + +static int hmac_interpose(lua_State *L) { + return interpose(L, HMAC_CLASS); +} /* hmac_interpose() */ + + +static void hmac_update_(lua_State *L, HMAC_CTX *ctx, int from, int to) { + int i; + + for (i = from; i <= to; i++) { + const void *p; + size_t n; + + p = luaL_checklstring(L, i, &n); + + HMAC_Update(ctx, p, n); + } +} /* hmac_update_() */ + + +static int hmac_update(lua_State *L) { + HMAC_CTX *ctx = luaL_checkudata(L, 1, HMAC_CLASS); + + hmac_update_(L, ctx, 2, lua_gettop(L)); + + lua_pushboolean(L, 1); + + return 1; +} /* hmac_update() */ + + +static int hmac_final(lua_State *L) { + HMAC_CTX *ctx = luaL_checkudata(L, 1, HMAC_CLASS); + unsigned char hmac[EVP_MAX_MD_SIZE]; + unsigned len; + + hmac_update_(L, ctx, 2, lua_gettop(L)); + + HMAC_Final(ctx, hmac, &len); + + lua_pushlstring(L, (char *)hmac, len); + + return 1; +} /* hmac_final() */ + + +static int hmac__gc(lua_State *L) { + HMAC_CTX *ctx = luaL_checkudata(L, 1, HMAC_CLASS); + + HMAC_CTX_cleanup(ctx); + + return 0; +} /* hmac__gc() */ + + +static const luaL_Reg hmac_methods[] = { + { "update", &hmac_update }, + { "final", &hmac_final }, + { NULL, NULL }, +}; + +static const luaL_Reg hmac_metatable[] = { + { "__gc", &hmac__gc }, + { NULL, NULL }, +}; + +static const luaL_Reg hmac_globals[] = { + { "new", &hmac_new }, + { "interpose", &hmac_interpose }, + { NULL, NULL }, +}; + +int luaopen__openssl_hmac(lua_State *L) { + initall(L); + + luaL_newlib(L, hmac_globals); + + return 1; +} /* luaopen__openssl_hmac() */ + + +/* + * Cipher - openssl.cipher + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static const EVP_CIPHER *cipher_checktype(lua_State *L, int index) { + const char *name = luaL_checkstring(L, index); + const EVP_CIPHER *type; + + if (!(type = EVP_get_cipherbyname(name))) + luaL_argerror(L, index, lua_pushfstring(L, "%s: invalid cipher type", name)); + + return type; +} /* cipher_checktype() */ + + +static int cipher_new(lua_State *L) { + const EVP_CIPHER *type; + EVP_CIPHER_CTX *ctx; + + type = cipher_checktype(L, 1); + + ctx = prepudata(L, sizeof *ctx, CIPHER_CLASS, NULL); + EVP_CIPHER_CTX_init(ctx); + + if (!EVP_CipherInit_ex(ctx, type, NULL, NULL, NULL, -1)) + return throwssl(L, "cipher.new"); + + return 1; +} /* cipher_new() */ + + +static int cipher_interpose(lua_State *L) { + return interpose(L, HMAC_CLASS); +} /* cipher_interpose() */ + + +static int cipher_init(lua_State *L, _Bool encrypt) { + EVP_CIPHER_CTX *ctx = luaL_checkudata(L, 1, CIPHER_CLASS); + const void *key, *iv; + size_t n, m; + + key = luaL_checklstring(L, 2, &n); + m = (size_t)EVP_CIPHER_CTX_key_length(ctx); + luaL_argcheck(L, n == m, 2, lua_pushfstring(L, "%d: invalid key length (should be %d)", (int)n, (int)m)); + + iv = luaL_optlstring(L, 3, NULL, &n); + m = (size_t)EVP_CIPHER_CTX_iv_length(ctx); + luaL_argcheck(L, n == m, 3, lua_pushfstring(L, "%d: invalid IV length (should be %d)", (int)n, (int)m)); + + if (!EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, encrypt)) + goto sslerr; + + if (!lua_isnoneornil(L, 4)) { + luaL_checktype(L, 4, LUA_TBOOLEAN); + + if (!EVP_CIPHER_CTX_set_padding(ctx, lua_toboolean(L, 4))) + goto sslerr; + } + + lua_settop(L, 1); + + return 1; +sslerr: + return throwssl(L, (encrypt)? "cipher:encrypt" : "cipher:decrypt"); +} /* cipher_init() */ + + +static int cipher_encrypt(lua_State *L) { + return cipher_init(L, 1); +} /* cipher_encrypt() */ + + +static int cipher_decrypt(lua_State *L) { + return cipher_init(L, 0); +} /* cipher_decrypt() */ + + +static _Bool cipher_update_(lua_State *L, EVP_CIPHER_CTX *ctx, luaL_Buffer *B, int from, int to) { + const unsigned char *p, *pe; + size_t block, step, n; + int i; + + block = EVP_CIPHER_CTX_block_size(ctx); + + if (LUAL_BUFFERSIZE < block * 2) + luaL_error(L, "cipher:update: LUAL_BUFFERSIZE(%d) < 2 * EVP_CIPHER_CTX_block_size(%d)", (int)LUAL_BUFFERSIZE, (int)block); + + step = LUAL_BUFFERSIZE - block; + + for (i = from; i <= to; i++) { + p = (const unsigned char *)luaL_checklstring(L, i, &n); + pe = p + n; + + while (p < pe) { + int in = (int)MIN((size_t)(pe - p), step), out; + + if (!EVP_CipherUpdate(ctx, (void *)luaL_prepbuffer(B), &out, p, in)) + return 0; + + p += in; + luaL_addsize(B, out); + } + } + + return 1; +} /* cipher_update_() */ + + +static int cipher_update(lua_State *L) { + EVP_CIPHER_CTX *ctx = luaL_checkudata(L, 1, CIPHER_CLASS); + luaL_Buffer B; + + luaL_buffinit(L, &B); + + if (!cipher_update_(L, ctx, &B, 2, lua_gettop(L))) + goto sslerr; + + luaL_pushresult(&B); + + return 1; +sslerr: + lua_pushnil(L); + pusherror(L, NULL); + + return 2; +} /* cipher_update() */ + + +static int cipher_final(lua_State *L) { + EVP_CIPHER_CTX *ctx = luaL_checkudata(L, 1, CIPHER_CLASS); + luaL_Buffer B; + size_t block; + int out; + + luaL_buffinit(L, &B); + + if (!cipher_update_(L, ctx, &B, 2, lua_gettop(L))) + goto sslerr; + + block = EVP_CIPHER_CTX_block_size(ctx); + + if (LUAL_BUFFERSIZE < block) + return luaL_error(L, "cipher:update: LUAL_BUFFERSIZE(%d) < EVP_CIPHER_CTX_block_size(%d)", (int)LUAL_BUFFERSIZE, (int)block); + + if (!EVP_CipherFinal(ctx, (void *)luaL_prepbuffer(&B), &out)) + goto sslerr; + + luaL_addsize(&B, out); + luaL_pushresult(&B); + + return 1; +sslerr: + lua_pushnil(L); + pusherror(L, NULL); + + return 2; +} /* cipher_final() */ + + +static int cipher__gc(lua_State *L) { + EVP_CIPHER_CTX *ctx = luaL_checkudata(L, 1, CIPHER_CLASS); + + EVP_CIPHER_CTX_cleanup(ctx); + + return 0; +} /* cipher__gc() */ + + +static const luaL_Reg cipher_methods[] = { + { "encrypt", &cipher_encrypt }, + { "decrypt", &cipher_decrypt }, + { "update", &cipher_update }, + { "final", &cipher_final }, + { NULL, NULL }, +}; + +static const luaL_Reg cipher_metatable[] = { + { "__gc", &cipher__gc }, + { NULL, NULL }, +}; + +static const luaL_Reg cipher_globals[] = { + { "new", &cipher_new }, + { "interpose", &cipher_interpose }, + { NULL, NULL }, +}; + +int luaopen__openssl_cipher(lua_State *L) { + initall(L); + + luaL_newlib(L, cipher_globals); + + return 1; +} /* luaopen__openssl_cipher() */ + + +/* + * Rand - openssl.rand + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static int rand_bytes(lua_State *L) { + int size = luaL_checkint(L, 1); + luaL_Buffer B; + int count = 0, n; + + luaL_buffinit(L, &B); + + while (count < size) { + n = MIN((size - count), LUAL_BUFFERSIZE); + + if (!RAND_bytes((void *)luaL_prepbuffer(&B), n)) + return throwssl(L, "rand.bytes"); + + luaL_addsize(&B, n); + count += n; + } + + luaL_pushresult(&B); + + return 1; +} /* rand_bytes() */ + + +static int rand_ready(lua_State *L) { + lua_pushboolean(L, RAND_status() == 1); + + return 1; +} /* rand_ready() */ + + +static unsigned long long rand_llu(lua_State *L) { + unsigned long long llu; + + if (!RAND_bytes((void *)&llu, sizeof llu)) + throwssl(L, "rand.uniform"); + + return llu; +} /* rand_llu() */ + +/* + * The following algorithm for rand_uniform() is taken from OpenBSD's + * arc4random_uniform, written by Otto Moerbeek, with subsequent + * simplification by Jorden Verwer. Otto's source code comment reads + * + * Uniformity is achieved by generating new random numbers until the one + * returned is outside the range [0, 2**32 % upper_bound). This guarantees + * the selected random number will be inside [2**32 % upper_bound, 2**32) + * which maps back to [0, upper_bound) after reduction modulo upper_bound. + * + * -- + * A more bit-efficient approach by the eminent statistician Herman Rubin + * can be found in this sci.crypt Usenet post. + * + * From: hrubin@odds.stat.purdue.edu (Herman Rubin) + * Newsgroups: sci.crypt + * Subject: Re: Generating a random number between 0 and N-1 + * Date: 14 Nov 2002 11:20:37 -0500 + * Organization: Purdue University Statistics Department + * Lines: 40 + * Message-ID: <ar0igl$1ak2@odds.stat.purdue.edu> + * References: <yO%y9.19646$RO1.373975@weber.videotron.net> <3DCD8D75.40408@nospam.com> + * NNTP-Posting-Host: odds.stat.purdue.edu + * X-Trace: mozo.cc.purdue.edu 1037290837 9316 128.210.141.13 (14 Nov 2002 16:20:37 GMT) + * X-Complaints-To: ne...@news.purdue.edu + * NNTP-Posting-Date: Thu, 14 Nov 2002 16:20:37 +0000 (UTC) + * Xref: archiver1.google.com sci.crypt:78935 + * + * In article <3DCD8D7...@nospam.com>, + * Michael Amling <nos...@nospam.com> wrote: + * >Carlos Moreno wrote: + * + * I have already posted on this, but a repeat might be + * in order. + * + * If one can trust random bits, the most bitwise efficient + * manner to get a single random integer between 0 and N-1 + * can be obtained as follows; the code can be made more + * computationally efficient. I believe it is easier to + * understand with gotos. I am assuming N>1. + * + * i = 0; j = 1; + * + * loop: j=2*j; i=2*i+RANBIT; + * if (j < N) goto loop; + * if (i >= N) { + * i = i - N; + * j = j - N; + * goto loop:} + * else return (i); + * + * The algorithm works because at each stage i is uniform + * between 0 and j-1. + * + * Another possibility is to generate k bits, where 2^k >= N. + * If 2^k = c*N + remainder, generate the appropriate value + * if a k-bit random number is less than c*N. + * + * For N = 17 (numbers just larger than powers of 2 are "bad"), + * the amount of information is about 4.09 bits, the best + * algorithm to generate one random number takes about 5.765 + * bits, taking k = 5 uses 9.412 bits, taking k = 6 or 7 uses + * 7.529 bits. These are averages, but the tails are not bad. + * + * (https://groups.google.com/forum/message/raw?msg=sci.crypt/DMslf6tSrD8/rv9rk6oP3r4J) + */ +static int rand_uniform(lua_State *L) { + if (lua_isnoneornil(L, 1)) { + unsigned long long r = rand_llu(L); + + lua_pushnumber(L, r); + + return 1; + } else { + unsigned long long N = luaL_checknumber(L, 1); + unsigned long long r, m; + + luaL_argcheck(L, N > 1, 1, lua_pushfstring(L, "[0, %d): interval is empty", (int)N)); + + m = -N % N; + + do { + r = rand_llu(L); + } while (r < m); + + lua_pushnumber(L, (r % N)); + + return 1; + } +} /* rand_uniform() */ + + +static const luaL_Reg rand_globals[] = { + { "bytes", &rand_bytes }, + { "ready", &rand_ready }, + { "uniform", &rand_uniform }, + { NULL, NULL }, +}; + +int luaopen__openssl_rand(lua_State *L) { + initall(L); + + luaL_newlib(L, rand_globals); + + return 1; +} /* luaopen__openssl_rand() */ + + +static void initall(lua_State *L) { + ERR_load_crypto_strings(); + OpenSSL_add_all_algorithms(); + + addclass(L, BIGNUM_CLASS, bn_methods, bn_metatable); + addclass(L, PUBKEY_CLASS, pk_methods, pk_metatable); + addclass(L, X509_NAME_CLASS, xn_methods, xn_metatable); + addclass(L, X509_GENS_CLASS, gn_methods, gn_metatable); + addclass(L, X509_CERT_CLASS, xc_methods, xc_metatable); + addclass(L, X509_CSR_CLASS, xr_methods, xr_metatable); + addclass(L, X509_CHAIN_CLASS, xl_methods, xl_metatable); + addclass(L, X509_STORE_CLASS, xs_methods, xs_metatable); + addclass(L, SSL_CTX_CLASS, sx_methods, sx_metatable); + addclass(L, SSL_CLASS, ssl_methods, ssl_metatable); + addclass(L, DIGEST_CLASS, md_methods, md_metatable); + addclass(L, HMAC_CLASS, hmac_methods, hmac_metatable); + addclass(L, CIPHER_CLASS, cipher_methods, cipher_metatable); +} /* initall() */ + + +#endif /* L_OPENSSL_H */ |