diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..160b0ae --- /dev/null +++ b/.travis.yml @@ -0,0 +1,39 @@ +language: erlang + +env: + global: + - LUAROCKS_BASE=luarocks-2.0.13 + matrix: + - "LUA=lua5.1 LUA_DEV=liblua5.1-dev LUA_VER=5.1 LUA_SFX=5.1 LUA_INCDIR=/usr/include/lua5.1" + - "LUA=lua5.2 LUA_DEV=liblua5.2-dev LUA_VER=5.2 LUA_SFX=5.2 LUA_INCDIR=/usr/include/lua5.2" + - "LUA=luajit LUA_DEV=libluajit-5.1-dev LUA_VER=5.1 LUA_SFX=jit-2.0.0-beta9 LUA_INCDIR=/usr/include/luajit-2.0" + +branches: + only: + - stream + +before_install: + - "sudo apt-get install $LUA" + - "sudo apt-get install $LUA_DEV" + - "lua$LUA_SFX -v" + # Install a recent luarocks release + - wget http://luarocks.org/releases/$LUAROCKS_BASE.tar.gz + - tar zxvpf $LUAROCKS_BASE.tar.gz + - cd $LUAROCKS_BASE + - ./configure + --lua-version=$LUA_VER --lua-suffix=$LUA_SFX --with-lua-include="$LUA_INCDIR" + - sudo make + - sudo make install + - cd $TRAVIS_BUILD_DIR + +install: + - sudo apt-get install openssl + - sudo luarocks install lunitx + - sudo luarocks make rockspecs/luacrypto-git-1.rockspec + +script: "cd tests/unit && lunit.sh -i lua$LUA_SFX run.lua" + +notifications: + email: + on_success: change + on_failure: always diff --git a/rockspecs/luacrypto-git-1.rockspec b/rockspecs/luacrypto-git-1.rockspec index 2d428cc..d0c7704 100644 --- a/rockspecs/luacrypto-git-1.rockspec +++ b/rockspecs/luacrypto-git-1.rockspec @@ -10,9 +10,14 @@ digests (MD5, SHA-1, HMAC, and more), encryption, decryption and crypto-grade ra dependencies = { "lua >= 5.1", } +external_dependencies = { + OPENSSL = { + header = "openssl/evp.h" + } +} source = { - url = [[git://github.com/mkottman/luacrypto.git]], - dir = "luacrypto" + url = "https://github.com/mkottman/luacrypto/archive/master.zip", + dir = "luacrypto-master", } build = { platforms = { @@ -22,12 +27,14 @@ build = { install_command = [[copy ".\Release\crypto.dll" "$(LIBDIR)\crypto.dll" /y ]] }, unix = { - type = "make", - variables = { - INCONCERT_DEVEL = "$(INCONCERT_DEVEL)", - LUA_LUADIR = "$(LUADIR)", - LUA_LIBDIR = "$(LIBDIR)", - LUA_PREFIX = "$(PREFIX)" + type = "builtin", + modules = { + crypto = { + sources = "src/lcrypto.c", + incdirs = "$(OPENSSL_INCDIR)", + libdirs = "$(OPENSSL_LIBDIR)", + libraries = "crypto", + } } } }, diff --git a/src/lcrypto.c b/src/lcrypto.c index 7de5cdd..5011788 100644 --- a/src/lcrypto.c +++ b/src/lcrypto.c @@ -45,6 +45,60 @@ static void luaL_register (lua_State *L, const char *libname, const luaL_Reg *l) #endif +#ifdef LCRYPTO_USE_TEMP_BUFFERS +# ifndef LCRYPTO_TEMP_BUFFER_SIZE +# define LCRYPTO_TEMP_BUFFER_SIZE 128 +# endif +# define LCRYPTO_ALLOC_TEMP(BUF, SIZE) (sizeof(BUF) >= SIZE)?(BUF):malloc(SIZE) +# define LCRYPTO_FREE_TEMP(BUF, PTR) do{if((PTR) != (BUF))free((void*)PTR);}while(0) +# define LCRYPTO_DEFINE_TEMP_BUFFER(NAME) char NAME[LCRYPTO_TEMP_BUFFER_SIZE] +#else +# define LCRYPTO_ALLOC_TEMP(BUF, SIZE) malloc(SIZE) +# define LCRYPTO_FREE_TEMP(BUF, PTR) free((void*)PTR) +# define LCRYPTO_DEFINE_TEMP_BUFFER(NAME) static const void *const NAME = NULL +#endif + +#define LCRYPTO_STATIC_ASSERT(cond) do{int a[(cond)?1:0]; (void*)a;}while(0) + +/* return [ buffer[be], buffer[en] ] */ +static const char* correct_range(lua_State *L, size_t *size){ + if(lua_islightuserdata(L, 2)){ + const char *input = (const char*)lua_touserdata(L, 2); + int be = luaL_checkinteger(L, 3); + int en = luaL_checkinteger(L, 4); + lua_pop(L, 2); + luaL_argcheck(L, be > 0, 3, "invalid begin index"); + luaL_argcheck(L, en > 0, 4, "invalid end index"); + if(be > en) be = en; + *size = en - be + 1; + return input + be - 1; + } + else{ + size_t len; const char *input = luaL_checklstring(L, 2, &len); + int be, en; + if(lua_isnumber(L, 3)){ + be = lua_tointeger(L, 3); + lua_remove(L, 3); + }else be = 1; + if(lua_isnumber(L, 3)){ + en = lua_tointeger(L, 3); + lua_remove(L, 3); + }else en = len; + luaL_argcheck(L, be > 0, 3, "invalid begin index"); + luaL_argcheck(L, en > 0, 4, "invalid end index"); + if((size_t)en > len) en = len; + if(be > en) be = en; + *size = en - be + 1; + return input + be - 1; + } +} + +static const char* opt_correct_range(lua_State *L, size_t *size){ + if( lua_isstring(L, 2) || lua_islightuserdata(L, 2) ) + return correct_range(L, size); + return NULL; +} + static void luacrypto_register_submodule(lua_State *L, const char *libname, const luaL_Reg *l) { int top = lua_gettop(L); @@ -75,6 +129,7 @@ static int crypto_error(lua_State *L) } /*************** DIGEST API ***************/ +//{ static EVP_MD_CTX *digest_pnew(lua_State *L) { @@ -133,7 +188,7 @@ static int digest_update(lua_State *L) { EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_DIGESTNAME); size_t slen; - const char *s = luaL_checklstring(L, 2, &slen); + const char *s = correct_range(L, &slen); if (!EVP_DigestUpdate(c, s, slen)) { @@ -153,23 +208,23 @@ static int digest_final(lua_State *L) unsigned int i; char *hex; - if (lua_isstring(L, 2)) - { - size_t slen; - const char *s = luaL_checklstring(L, 2, &slen); - if (!EVP_DigestUpdate(c, s, slen)) - { - return crypto_error(L); + { size_t len; const char *s = opt_correct_range(L, &len); + if(s){ + if(!EVP_DigestUpdate(c, s, len)){ + return crypto_error(L); + } } } d = EVP_MD_CTX_create(); if (!EVP_MD_CTX_copy_ex(d, c)) { + EVP_MD_CTX_destroy(d); return crypto_error(L); } if (!EVP_DigestFinal_ex(d, digest, &written)) { + EVP_MD_CTX_destroy(d); return crypto_error(L); } EVP_MD_CTX_destroy(d); @@ -256,10 +311,223 @@ static int digest_fdigest(lua_State *L) return 1; } -/*************** ENCRYPT API ***************/ +//} + +/*************** EVP_CIPHER API ***************/ +//{ + +#ifndef LCRYPTO_MAX_BUFFER_SIZE +# define LCRYPTO_MAX_BUFFER_SIZE 4096 +#endif + +typedef struct l_evp_cipher_ctx_tag +{ + EVP_CIPHER_CTX ctx[1]; + int cb_ref, ud_ref; +} l_evp_cipher_ctx; + +typedef int (*pfinit_fun) (EVP_CIPHER_CTX *, const EVP_CIPHER *, ENGINE *, const unsigned char *, const unsigned char *); + +typedef int (*pfcrypt_update)(EVP_CIPHER_CTX*, unsigned char*, int*, const unsigned char*, int); + +typedef int (*pfcrypt_final)(EVP_CIPHER_CTX*, unsigned char*, int*); + +static void encrypt_static_asserts(void) +{ + // ensure convert (EVP_CIPHER_CTX*) <=> (l_evp_cipher_ctx*) + LCRYPTO_STATIC_ASSERT(offsetof(l_evp_cipher_ctx, ctx) == 0); +} + +static int evp_cipher_ctx_set_writer(lua_State *L, l_evp_cipher_ctx *ctx) +{ + if(ctx->ud_ref != LUA_NOREF) + { + luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud_ref); + ctx->ud_ref = LUA_NOREF; + } + + if(ctx->cb_ref != LUA_NOREF) + { + luaL_unref(L, LUA_REGISTRYINDEX, ctx->cb_ref); + ctx->cb_ref = LUA_NOREF; + } + + if(lua_gettop(L) >= 3) // reader + context + { + lua_settop(L, 3); + luaL_argcheck(L, !lua_isnil(L, 2), 2, "no writer present"); + ctx->ud_ref = luaL_ref(L, LUA_REGISTRYINDEX); + ctx->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); + assert(1 == lua_gettop(L)); + return 1; + } + + // only writer (object or function) + lua_settop(L, 2); + + if( lua_isnoneornil(L, 2) ) + { + lua_pop(L, 1); + assert(1 == lua_gettop(L)); + return 1; + } + + if(lua_isfunction(L, 2)) + { + ctx->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); + assert(1 == lua_gettop(L)); + return 1; + } + + if(lua_isuserdata(L, 2) || lua_istable(L, 2)) + { + lua_getfield(L, 2, "write"); + luaL_argcheck(L, lua_isfunction(L, -1), 2, "write method not found in object"); + ctx->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); + ctx->ud_ref = luaL_ref(L, LUA_REGISTRYINDEX); + assert(1 == lua_gettop(L)); + return 1; + } + + lua_pushliteral(L, "invalid writer type"); + return lua_error(L); +} + +static int evp_cipher_ctx_get_writer(lua_State *L, l_evp_cipher_ctx *ctx) +{ + lua_rawgeti(L, LUA_REGISTRYINDEX, ctx->cb_ref); + lua_rawgeti(L, LUA_REGISTRYINDEX, ctx->ud_ref); + return 2; +} + +static int evp_cipher_ctx_push_cb(lua_State *L, l_evp_cipher_ctx *ctx) +{ + assert(ctx->cb_ref != LUA_NOREF); + lua_rawgeti(L, LUA_REGISTRYINDEX, ctx->cb_ref); + if(ctx->ud_ref != LUA_NOREF) + { + lua_rawgeti(L, LUA_REGISTRYINDEX, ctx->ud_ref); + return 2; + } + return 1; +} + +static int evp_cipher_ctx_cleanup(lua_State *L, l_evp_cipher_ctx *ctx) +{ + if (!EVP_CIPHER_CTX_cleanup(ctx->ctx)) + { + return crypto_error(L); + } + luaL_unref(L, LUA_REGISTRYINDEX, ctx->cb_ref); + luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud_ref); + ctx->cb_ref = ctx->ud_ref = LUA_NOREF; + return 0; +} + +static int evp_cipher_ctx_update(lua_State *L, l_evp_cipher_ctx *ctx, pfcrypt_update pfupdate, const char *input, size_t input_len) +{ + size_t buf_len = input_len + (size_t)EVP_CIPHER_CTX_block_size(ctx->ctx); + if(buf_len <= LCRYPTO_MAX_BUFFER_SIZE){ + LCRYPTO_DEFINE_TEMP_BUFFER(buf); + int n; + unsigned char *buffer = (unsigned char *)LCRYPTO_ALLOC_TEMP(buf, buf_len); + int output_len = 0; + + if (!pfupdate(ctx->ctx, buffer, &output_len, (unsigned char *)input, (int)input_len)) + { + LCRYPTO_FREE_TEMP(buf, buffer); + return crypto_error(L); + } + + if(ctx->cb_ref == LUA_NOREF) + { + lua_pushlstring(L, (char *)buffer, (size_t)output_len); + LCRYPTO_FREE_TEMP(buf, buffer); + return 1; + } + + n = evp_cipher_ctx_push_cb(L, ctx); + lua_pushlstring(L, (char *)buffer, (size_t)output_len); + LCRYPTO_FREE_TEMP(buf, buffer); + lua_call(L, n, 0); + lua_settop(L, 1); + return 1; + } + //----------------------------------------------------------------- + {size_t chunk_size = LCRYPTO_MAX_BUFFER_SIZE - (size_t)EVP_CIPHER_CTX_block_size(ctx->ctx); + const int use_buffer = (ctx->cb_ref == LUA_NOREF)?1:0; + const char *b, *e; + int n; + luaL_Buffer buffer; + unsigned char *tmp = (unsigned char *)malloc(LCRYPTO_MAX_BUFFER_SIZE); + + if(use_buffer) luaL_buffinit(L, &buffer); + else n = evp_cipher_ctx_push_cb(L, ctx); + for(b = input, e = input + input_len; b < e; b += chunk_size) + { + size_t left = e - b; + int output_len = 0; + if(left > chunk_size) left = chunk_size; + + memcpy(tmp, b, left); + if (!pfupdate(ctx->ctx, tmp, &output_len, (unsigned char *)b, (int)left)) + { + free(tmp); + return crypto_error(L); + } + + if(use_buffer) luaL_addlstring(&buffer, (char*)tmp, output_len); + else{ + int i, top = lua_gettop(L); + for(i = n; i > 0; --i) lua_pushvalue(L, top - i + 1); + lua_pushlstring(L, (char*)tmp, output_len); + //! @todo use traceback to catch error call stack + if(0 != lua_pcall(L, n, 0, 0)){ + free(tmp); + return lua_error(L); + } + } + } + free(tmp); + + if(use_buffer){ + luaL_pushresult(&buffer); + return 1; + }} + + lua_settop(L, 1); + return 1; +} + +static int evp_cipher_ctx_final(lua_State *L, l_evp_cipher_ctx *ctx, pfcrypt_final pffinal) +{ + int n, output_len = 0; + unsigned char buffer[EVP_MAX_BLOCK_LENGTH]; + + if (!pffinal(ctx->ctx, buffer, &output_len)) + { + return crypto_error(L); + } + + if(ctx->cb_ref == LUA_NOREF) + { + lua_pushlstring(L, (char *)buffer, (size_t)output_len); + return 1; + } + + n = evp_cipher_ctx_push_cb(L, ctx); + lua_pushlstring(L, (char *)buffer, (size_t)output_len); + lua_call(L, n, 0); + return 0; +} + +#define TRY_CTX(fun) if (!fun) { *size_to_return = crypto_error(L); return 0; } + +typedef l_evp_cipher_ctx (*pfencrypt_new)(lua_State *); static int parse_enc_params(lua_State *L, EVP_CIPHER **cipher, char **key, size_t *key_len, char **iv, size_t *iv_len, int *pad, int *size_to_return, + char *key_buf, char *iv_buf, int cipher_pos, int key_pos, int iv_pos, int pad_pos) { const char *s = luaL_checkstring(L, cipher_pos); @@ -272,16 +540,57 @@ static int parse_enc_params(lua_State *L, EVP_CIPHER **cipher, char **key, size_ } *key_len = 0; - *key = (char *)luaL_checklstring(L, key_pos, key_len); - if (*key_len > EVP_MAX_KEY_LENGTH) + + if(lua_istable(L, key_pos)) { - return luaL_argerror(L, key_pos, "invalid encrypt/decrypt key"); + size_t i; + *key_len = lua_objlen(L, key_pos); + if (*key_len > EVP_MAX_KEY_LENGTH) + { + return luaL_argerror(L, key_pos, "invalid encrypt/decrypt key"); + } + for(i = 0; i<*key_len; ++i) + { + lua_rawgeti(L, key_pos, i+1); + key_buf[i] = (char)luaL_checkint(L,-1); + lua_pop(L, 1); + } + *key = &key_buf[0]; + } + else + { + *key = (char*)luaL_checklstring(L, key_pos, key_len); + if (*key_len > EVP_MAX_KEY_LENGTH) + { + return luaL_argerror(L, key_pos, "invalid encrypt/decrypt key"); + } } *iv_len = 0; *iv = (char *)lua_tolstring(L, iv_pos, iv_len); /* can be NULL */ - if (*iv && (*iv_len > (size_t)EVP_CIPHER_iv_length(*cipher))) - return luaL_argerror(L, iv_pos, "invalid iv length"); + if(*iv) + { + if(*iv_len > (size_t)EVP_CIPHER_iv_length(*cipher)) + { + return luaL_argerror(L, iv_pos, "invalid iv length"); + } + } + else if (lua_istable(L, iv_pos)) + { + size_t i; + *iv_len = lua_objlen(L, iv_pos); + if(*iv_len > (size_t)EVP_CIPHER_iv_length(*cipher)) + { + return luaL_argerror(L, iv_pos, "invalid iv length"); + } + for(i = 0; i<*iv_len; ++i) + { + lua_rawgeti(L, iv_pos, i+1); + iv_buf[i] = (char)luaL_checkint(L,-1); + lua_pop(L, 1); + } + *iv = &iv_buf[0]; + } *pad = (lua_gettop(L) < pad_pos || lua_toboolean(L, pad_pos)); @@ -289,22 +598,22 @@ static int parse_enc_params(lua_State *L, EVP_CIPHER **cipher, char **key, size_ } static int parse_new_enc_params(lua_State *L, EVP_CIPHER **cipher, char **key, size_t *key_len, - char **iv, size_t *iv_len, int *pad, int *size_to_return) + char **iv, size_t *iv_len, int *pad, int *size_to_return, + char *key_buf, char *iv_buf) { - return parse_enc_params(L, cipher, key, key_len, iv, iv_len, pad, size_to_return, + return parse_enc_params(L, cipher, key, key_len, iv, iv_len, pad, size_to_return,key_buf,iv_buf, 1, 2, 3, 4); } static int parse_f_enc_params(lua_State *L, EVP_CIPHER **cipher, char **key, size_t *key_len, - char **iv, size_t *iv_len, int *pad, int *size_to_return) + char **iv, size_t *iv_len, int *pad, int *size_to_return, + char *key_buf, char *iv_buf) { - return parse_enc_params(L, cipher, key, key_len, iv, iv_len, pad, size_to_return, + return parse_enc_params(L, cipher, key, key_len, iv, iv_len, pad, size_to_return, key_buf, iv_buf, 2, 4, 5, 6); } -#define TRY_CTX(fun) if (!fun) { *size_to_return = crypto_error(L); return 0; } - -static int init_encryptor_decryptor(int (*init_fun)(EVP_CIPHER_CTX *, const EVP_CIPHER *, ENGINE *, const unsigned char *, const unsigned char *), +static int init_encryptor_decryptor(pfinit_fun init_fun, lua_State *L, EVP_CIPHER_CTX *c, const EVP_CIPHER *cipher, const char *key, size_t key_len, const char *iv, size_t iv_len, int pad, int *size_to_return) { @@ -317,8 +626,8 @@ static int init_encryptor_decryptor(int (*init_fun)(EVP_CIPHER_CTX *, const EVP_ if (!pad) TRY_CTX(EVP_CIPHER_CTX_set_padding(c, 0)) - if (iv) - memcpy(the_iv, iv, iv_len); + if (iv) + memcpy(the_iv, iv, iv_len); memcpy(the_key, key, key_len); TRY_CTX(init_fun(c, NULL, NULL, the_key, the_iv)) @@ -326,93 +635,139 @@ static int init_encryptor_decryptor(int (*init_fun)(EVP_CIPHER_CTX *, const EVP_ return 1; } -static EVP_CIPHER_CTX *encrypt_pnew(lua_State *L) +static l_evp_cipher_ctx *encrypt_pnew_impl(lua_State *L, const char *tname) { - EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)lua_newuserdata(L, sizeof(EVP_CIPHER_CTX)); - luaL_getmetatable(L, LUACRYPTO_ENCRYPTNAME); + l_evp_cipher_ctx *c = (l_evp_cipher_ctx *)lua_newuserdata(L, sizeof(l_evp_cipher_ctx)); + c->cb_ref = c->ud_ref = LUA_NOREF; + luaL_getmetatable(L, tname); lua_setmetatable(L, -2); return c; } -static int encrypt_fnew(lua_State *L) +static int encrypt_fnew_impl(lua_State *L, const char *tname, pfinit_fun pfinit_fun) { + char the_key[EVP_MAX_KEY_LENGTH] = {0}; + char the_iv[EVP_MAX_IV_LENGTH] = {0}; const char *key = 0, *iv = 0; const EVP_CIPHER *cipher = 0; size_t key_len = 0, iv_len = 0; int pad = 1, size_to_return = 0; - EVP_CIPHER_CTX *c; + l_evp_cipher_ctx *c; if (!parse_new_enc_params(L, (EVP_CIPHER **)&cipher, (char **)&key, &key_len, - (char **)&iv, &iv_len, &pad, &size_to_return)) + (char **)&iv, &iv_len, &pad, &size_to_return, the_key, the_iv)) { return size_to_return; } - c = encrypt_pnew(L); - if (!init_encryptor_decryptor(EVP_EncryptInit_ex, L, c, cipher, key, key_len, iv, iv_len, pad, &size_to_return)) + c = encrypt_pnew_impl(L, tname); + + if (!init_encryptor_decryptor(pfinit_fun, L, c->ctx, cipher, key, key_len, iv, iv_len, pad, &size_to_return)) { return size_to_return; } return 1; } -static int encrypt_update(lua_State *L) +static l_evp_cipher_ctx* ecrypt_get_at(lua_State *L, int i, const char *tname) { - EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)luaL_checkudata(L, 1, LUACRYPTO_ENCRYPTNAME); - size_t input_len = 0; - const unsigned char *input = (unsigned char *)luaL_checklstring(L, 2, &input_len); - int output_len = 0; - unsigned char *buffer = NULL; + l_evp_cipher_ctx *c = (l_evp_cipher_ctx *)luaL_checkudata(L, i, tname); + return c; +} - buffer = (unsigned char *)malloc(input_len + (size_t)EVP_CIPHER_CTX_block_size(c)); - if (!EVP_EncryptUpdate(c, buffer, &output_len, input, (int)input_len)) - { - free(buffer); - return crypto_error(L); - } - lua_pushlstring(L, (char *)buffer, (size_t)output_len); - free(buffer); +static int encrypt_set_writer_impl(lua_State *L, const char *tname) +{ + return evp_cipher_ctx_set_writer(L, ecrypt_get_at(L, 1, tname)); +} - return 1; +static int encrypt_get_writer_impl(lua_State *L, const char *tname) +{ + return evp_cipher_ctx_get_writer(L, ecrypt_get_at(L, 1, tname)); } -static int encrypt_final(lua_State *L) +static int encrypt_update_impl(lua_State *L, const char *tname, pfcrypt_update pfupdate) { - EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)luaL_checkudata(L, 1, LUACRYPTO_ENCRYPTNAME); - int output_len = 0; - unsigned char buffer[EVP_MAX_BLOCK_LENGTH]; + l_evp_cipher_ctx *ctx = ecrypt_get_at(L, 1, tname); + size_t input_len; + const char *input = correct_range(L, &input_len); + return evp_cipher_ctx_update(L, ctx, pfupdate, input, input_len); +} - if (!EVP_EncryptFinal_ex(c, buffer, &output_len)) +static int encrypt_final_impl(lua_State *L, const char *tname, pfcrypt_final pffinal) +{ + int ret = evp_cipher_ctx_final(L, ecrypt_get_at(L, 1, tname), pffinal); + if (ret == 0) { - return crypto_error(L); + lua_settop(L, 1); + return 1; } - lua_pushlstring(L, (char *)buffer, (size_t)output_len); - return 1; + return ret; } -static int encrypt_tostring(lua_State *L) +static int encrypt_tostring_impl(lua_State *L, const char *tname) { - EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)luaL_checkudata(L, 1, LUACRYPTO_ENCRYPTNAME); + l_evp_cipher_ctx *c = ecrypt_get_at(L, 1, tname); char s[64]; - sprintf(s, "%s %p", LUACRYPTO_ENCRYPTNAME, (void *)c); + sprintf(s, "%s %p", tname, (void *)c); lua_pushstring(L, s); return 1; } -static int encrypt_gc(lua_State *L) +static int encrypt_clone_impl(lua_State *L, const char *tname) { - EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)luaL_checkudata(L, 1, LUACRYPTO_ENCRYPTNAME); - if (!EVP_CIPHER_CTX_cleanup(c)) - { + l_evp_cipher_ctx *c = ecrypt_get_at(L, 1, tname); + l_evp_cipher_ctx *d = encrypt_pnew_impl(L, tname); + EVP_CIPHER_CTX_init(d->ctx); + if(!EVP_CIPHER_CTX_copy(d->ctx, c->ctx)) return crypto_error(L); - } return 1; } -static int encrypt_fencrypt(lua_State *L) +static int encrypt_resetiv_impl(lua_State *L, const char *tname) { + l_evp_cipher_ctx *c = ecrypt_get_at(L, 1, tname); + size_t iv_len, i; + const char *iv; + if(lua_istable(L, 2)) + { + iv_len = lua_objlen(L, 2); + if(iv_len > (size_t)EVP_CIPHER_iv_length(c->ctx->cipher)) + { + return luaL_argerror(L, 2, "invalid iv length"); + } + for(i = 0; ictx->iv[i] = (unsigned char)lua_tointeger(L,-1); + lua_pop(L, 1); + } + return 0; + } + else if( iv = (char*)lua_tolstring(L, 2, &iv_len) ) + { + if(iv_len > (size_t)EVP_CIPHER_iv_length(c->ctx->cipher)) + { + return luaL_argerror(L, 2, "invalid iv length"); + } + memcpy(&c->ctx->iv[0], iv, iv_len); + } + return luaL_argerror(L, 2, "invalid iv value"); +} + +static int encrypt_gc_impl(lua_State *L, const char *tname) +{ + return evp_cipher_ctx_cleanup(L, ecrypt_get_at(L, 1, tname)); +} + +static int encrypt_fencrypt_impl(lua_State *L, pfinit_fun pfinit_fun, pfcrypt_update pfupdate, pfcrypt_final pffinal) +{ + char the_key[EVP_MAX_KEY_LENGTH] = {0}; + char the_iv[EVP_MAX_IV_LENGTH] = {0}; + + LCRYPTO_DEFINE_TEMP_BUFFER(tmp_output); + /* parameter 1 is the 'crypto.encrypt' table */ const EVP_CIPHER *type; @@ -429,175 +784,154 @@ static int encrypt_fencrypt(lua_State *L) unsigned char *buffer; - if (!parse_f_enc_params(L, (EVP_CIPHER **)&type, (char **)&key, &key_len, (char **)&iv, &iv_len, &pad, &size_to_return)) + if (!parse_f_enc_params(L, (EVP_CIPHER **)&type, (char **)&key, &key_len, (char **)&iv, &iv_len, &pad, &size_to_return, the_key, the_iv)) { return size_to_return; } - if (!init_encryptor_decryptor(EVP_EncryptInit_ex, L, &c, type, key, key_len, iv, iv_len, pad, &size_to_return)) + if (!init_encryptor_decryptor(pfinit_fun, L, &c, type, key, key_len, iv, iv_len, pad, &size_to_return)) { return size_to_return; } - buffer = (unsigned char *)malloc(input_len + (size_t)EVP_CIPHER_CTX_block_size(&c)); + buffer = (unsigned char *)LCRYPTO_ALLOC_TEMP(tmp_output, input_len + (size_t)EVP_CIPHER_CTX_block_size(&c)); - if (!EVP_EncryptUpdate(&c, buffer, &len, input, (int)input_len)) + if (!pfupdate(&c, buffer, &len, input, (int)input_len)) { EVP_CIPHER_CTX_cleanup(&c); - free(buffer); + LCRYPTO_FREE_TEMP(tmp_output, buffer); return crypto_error(L); } output_len += len; - if (!EVP_EncryptFinal_ex(&c, &buffer[output_len], &len)) + if (!pffinal(&c, &buffer[output_len], &len)) { EVP_CIPHER_CTX_cleanup(&c); - free(buffer); + LCRYPTO_FREE_TEMP(tmp_output, buffer); return crypto_error(L); } output_len += len; lua_pushlstring(L, (char *)buffer, (size_t)output_len); - free(buffer); + LCRYPTO_FREE_TEMP(tmp_output, buffer); EVP_CIPHER_CTX_cleanup(&c); return 1; } -/*************** DECRYPT API ***************/ +//} + +/*************** ENCRYPT API ***************/ +//{ -static EVP_CIPHER_CTX *decrypt_pnew(lua_State *L) +static int encrypt_fnew(lua_State *L) { - EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)lua_newuserdata(L, sizeof(EVP_CIPHER_CTX)); - luaL_getmetatable(L, LUACRYPTO_DECRYPTNAME); - lua_setmetatable(L, -2); - return c; + return encrypt_fnew_impl(L, LUACRYPTO_ENCRYPTNAME, EVP_EncryptInit_ex); } -static int decrypt_fnew(lua_State *L) +static int encrypt_set_writer(lua_State *L) { - const char *key = 0, *iv = 0; - const EVP_CIPHER *cipher = 0; + return encrypt_set_writer_impl(L, LUACRYPTO_ENCRYPTNAME); +} - size_t key_len = 0, iv_len = 0; - int pad = 1, size_to_return = 0; - EVP_CIPHER_CTX *c; +static int encrypt_get_writer(lua_State *L) +{ + return encrypt_get_writer_impl(L, LUACRYPTO_ENCRYPTNAME); +} - if (!parse_new_enc_params(L, (EVP_CIPHER **)&cipher, (char **)&key, &key_len, - (char **)&iv, &iv_len, &pad, &size_to_return)) - { - return size_to_return; - } +static int encrypt_update(lua_State *L) +{ + return encrypt_update_impl(L, LUACRYPTO_ENCRYPTNAME, &EVP_EncryptUpdate); +} - c = decrypt_pnew(L); - if (!init_encryptor_decryptor(EVP_DecryptInit_ex, L, c, cipher, key, key_len, iv, iv_len, pad, &size_to_return)) - { - return size_to_return; - } - return 1; +static int encrypt_final(lua_State *L) +{ + return encrypt_final_impl(L, LUACRYPTO_ENCRYPTNAME, EVP_EncryptFinal_ex); } -static int decrypt_update(lua_State *L) +static int encrypt_tostring(lua_State *L) { - EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)luaL_checkudata(L, 1, LUACRYPTO_DECRYPTNAME); - size_t input_len = 0; - const unsigned char *input = (unsigned char *)luaL_checklstring(L, 2, &input_len); - int output_len = 0; - unsigned char *buffer = NULL; + return encrypt_tostring_impl(L, LUACRYPTO_ENCRYPTNAME); +} - buffer = (unsigned char *)malloc(input_len + (size_t)EVP_CIPHER_CTX_block_size(c)); - if (!EVP_DecryptUpdate(c, buffer, &output_len, input, (int)input_len)) - { - return crypto_error(L); - } - lua_pushlstring(L, (char *)buffer, (size_t)output_len); - free(buffer); +static int encrypt_gc(lua_State *L) +{ + return encrypt_gc_impl(L, LUACRYPTO_ENCRYPTNAME); +} - return 1; +static int encrypt_clone(lua_State *L) +{ + return encrypt_clone_impl(L, LUACRYPTO_ENCRYPTNAME); } -static int decrypt_final(lua_State *L) +static int encrypt_resetiv(lua_State *L) { - EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)luaL_checkudata(L, 1, LUACRYPTO_DECRYPTNAME); - int output_len = 0; - unsigned char buffer[EVP_MAX_BLOCK_LENGTH]; + return encrypt_resetiv_impl(L, LUACRYPTO_ENCRYPTNAME); +} - if (!EVP_DecryptFinal_ex(c, buffer, &output_len)) - { - return crypto_error(L); - } - lua_pushlstring(L, (char *)buffer, (size_t)output_len); - return 1; +static int encrypt_fencrypt(lua_State *L) +{ + return encrypt_fencrypt_impl(L, EVP_EncryptInit_ex, EVP_EncryptUpdate, EVP_EncryptFinal_ex); } -static int decrypt_tostring(lua_State *L) +//} + +/*************** DECRYPT API ***************/ +//{ + +static int decrypt_fnew(lua_State *L) { - EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)luaL_checkudata(L, 1, LUACRYPTO_DECRYPTNAME); - char s[64]; - sprintf(s, "%s %p", LUACRYPTO_DECRYPTNAME, (void *)c); - lua_pushstring(L, s); - return 1; + return encrypt_fnew_impl(L, LUACRYPTO_DECRYPTNAME, EVP_DecryptInit_ex); } -static int decrypt_gc(lua_State *L) +static int decrypt_set_writer(lua_State *L) { - EVP_CIPHER_CTX *c = (EVP_CIPHER_CTX *)luaL_checkudata(L, 1, LUACRYPTO_DECRYPTNAME); - if (!EVP_CIPHER_CTX_cleanup(c)) - { - return crypto_error(L); - } - return 1; + return encrypt_set_writer_impl(L, LUACRYPTO_DECRYPTNAME); } -static int decrypt_fdecrypt(lua_State *L) +static int decrypt_get_writer(lua_State *L) { - /* parameter 1 is the 'crypto.decrypt' table */ - size_t input_len = 0; - const unsigned char *input = (unsigned char *)luaL_checklstring(L, 3, &input_len); + return encrypt_get_writer_impl(L, LUACRYPTO_DECRYPTNAME); +} - const EVP_CIPHER *type; - size_t key_len = 0, iv_len = 0; - const char *key = NULL, *iv = NULL; - int pad = 0, size_to_return = 0; - EVP_CIPHER_CTX c; - unsigned char *buffer; - int output_len = 0; - int len = 0; +static int decrypt_update(lua_State *L) +{ + return encrypt_update_impl(L, LUACRYPTO_DECRYPTNAME, &EVP_DecryptUpdate); +} +static int decrypt_final(lua_State *L) +{ + return encrypt_final_impl(L, LUACRYPTO_DECRYPTNAME, EVP_DecryptFinal_ex); +} - if (!parse_f_enc_params(L, (EVP_CIPHER **)&type, (char **)&key, &key_len, (char **)&iv, &iv_len, &pad, &size_to_return)) - { - return size_to_return; - } +static int decrypt_tostring(lua_State *L) +{ + return encrypt_tostring_impl(L, LUACRYPTO_DECRYPTNAME); +} +static int decrypt_gc(lua_State *L) +{ + return encrypt_gc_impl(L, LUACRYPTO_DECRYPTNAME); +} - if (!init_encryptor_decryptor(EVP_DecryptInit_ex, L, &c, type, key, key_len, iv, iv_len, pad, &size_to_return)) - { - return size_to_return; - } +static int decrypt_clone(lua_State *L) +{ + return encrypt_clone_impl(L, LUACRYPTO_DECRYPTNAME); +} - buffer = (unsigned char *)malloc(input_len + (size_t)EVP_CIPHER_CTX_block_size(&c)); - if (!EVP_DecryptUpdate(&c, buffer, &len, input, (int)input_len)) - { - EVP_CIPHER_CTX_cleanup(&c); - free(buffer); - return crypto_error(L); - } - output_len += len; - if (!EVP_DecryptFinal_ex(&c, &buffer[len], &len)) - { - EVP_CIPHER_CTX_cleanup(&c); - free(buffer); - return crypto_error(L); - } - output_len += len; +static int decrypt_resetiv(lua_State *L) +{ + return encrypt_resetiv_impl(L, LUACRYPTO_DECRYPTNAME); +} - lua_pushlstring(L, (char *)buffer, (size_t)output_len); - free(buffer); - EVP_CIPHER_CTX_cleanup(&c); - return 1; +static int decrypt_fdecrypt(lua_State *L) +{ + return encrypt_fencrypt_impl(L, EVP_DecryptInit_ex, EVP_DecryptUpdate, EVP_DecryptFinal_ex); } +//} + /*************** HMAC API ***************/ +//{ static HMAC_CTX *hmac_pnew(lua_State *L) { @@ -628,7 +962,9 @@ static int hmac_clone(lua_State *L) { HMAC_CTX *c = (HMAC_CTX *)luaL_checkudata(L, 1, LUACRYPTO_HMACNAME); HMAC_CTX *d = hmac_pnew(L); - *d = *c; + if(!HMAC_CTX_copy(d, c)){ + return crypto_error(L); + } return 1; } @@ -643,9 +979,12 @@ static int hmac_update(lua_State *L) { HMAC_CTX *c = (HMAC_CTX *)luaL_checkudata(L, 1, LUACRYPTO_HMACNAME); size_t slen; - const char *s = luaL_checklstring(L, 2, &slen); + const char *s = correct_range(L, &slen); - HMAC_Update(c, (unsigned char *)s, slen); + if (!HMAC_Update(c, (unsigned char *)s, slen)) + { + return crypto_error(L); + } lua_settop(L, 1); return 1; @@ -659,14 +998,26 @@ static int hmac_final(lua_State *L) unsigned int i; char *hex; - if (lua_isstring(L, 2)) - { - size_t slen; - const char *s = luaL_checklstring(L, 2, &slen); - HMAC_Update(c, (unsigned char *)s, slen); + { size_t len; const char *s = opt_correct_range(L, &len); + if(s) + { + if(!HMAC_Update(c, (unsigned char *)s, len)) + { + return crypto_error(L); + } + } } - HMAC_Final(c, digest, &written); + { + HMAC_CTX d; + HMAC_CTX_init(&d); + if(!HMAC_CTX_copy(&d, c)){ + HMAC_CTX_cleanup(&d); + return crypto_error(L); + } + HMAC_Final(&d, digest, &written); + HMAC_CTX_cleanup(&d); + } if (lua_toboolean(L, 3)) lua_pushlstring(L, (char *)digest, written); @@ -740,7 +1091,10 @@ static int hmac_fdigest(lua_State *L) return 1; } +//} + /*************** SIGN API ***************/ +//{ static EVP_MD_CTX *sign_pnew(lua_State *L) { @@ -770,10 +1124,14 @@ static int sign_fnew(lua_State *L) static int sign_update(lua_State *L) { EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_SIGNNAME); - size_t input_len = 0; - const unsigned char *input = (unsigned char *)luaL_checklstring(L, 2, &input_len); + size_t input_len; + const unsigned char *input = (unsigned char *)correct_range(L, &input_len); + + if(!EVP_SignUpdate(c, input, input_len)) + { + return crypto_error(L); + } - EVP_SignUpdate(c, input, input_len); return 0; } @@ -850,7 +1208,10 @@ static int sign_fsign(lua_State *L) } } +//} + /*************** VERIFY API ***************/ +//{ static EVP_MD_CTX *verify_pnew(lua_State *L) { @@ -881,8 +1242,8 @@ static int verify_fnew(lua_State *L) static int verify_update(lua_State *L) { EVP_MD_CTX *c = (EVP_MD_CTX *)luaL_checkudata(L, 1, LUACRYPTO_VERIFYNAME); - size_t input_len = 0; - const unsigned char *input = (unsigned char *)luaL_checklstring(L, 2, &input_len); + size_t input_len; + const unsigned char *input = (unsigned char *)correct_range(L, &input_len); if (EVP_VerifyUpdate(c, input, input_len) != 1) return crypto_error(L); @@ -964,7 +1325,11 @@ static int verify_fverify(lua_State *L) return 1; } } + +//} + /*************** RAND API ***************/ +//{ static int rand_do_bytes(lua_State *L, int (*bytes)(unsigned char *, int)) { @@ -1038,11 +1403,15 @@ static int rand_write(lua_State *L) static int rand_cleanup(lua_State *L UNUSED ) { + (void*)L; RAND_cleanup(); return 0; } +//} + /*************** PKEY API ***************/ +//{ static EVP_PKEY **pkey_new(lua_State *L) { @@ -1217,7 +1586,7 @@ static int pkey_from_pem(lua_State *L) int ret; ret = BIO_puts(mem, key); - if (ret != strlen(key)) + if ((size_t)ret != strlen(key)) { goto error; } @@ -1279,33 +1648,41 @@ static int pkey_tostring(lua_State *L) return 1; } +//} + /*************** SEAL API ***************/ +//{ typedef struct seal_context { - EVP_CIPHER_CTX *ctx; + l_evp_cipher_ctx ctx; int eklen; unsigned char iv[EVP_MAX_IV_LENGTH]; unsigned char *ek; } seal_context; +static seal_context *seal_get_at(lua_State *L, int i) +{ + seal_context *c = (seal_context *)luaL_checkudata(L, i, LUACRYPTO_SEALNAME); + return c; +} + static seal_context *seal_pnew(lua_State *L) { seal_context *c = (seal_context *)lua_newuserdata(L, sizeof(seal_context)); + c->ctx.cb_ref = c->ctx.ud_ref = LUA_NOREF; luaL_getmetatable(L, LUACRYPTO_SEALNAME); lua_setmetatable(L, -2); - memset(c, 0, sizeof(seal_context)); - c->ctx = (EVP_CIPHER_CTX *)malloc(sizeof(EVP_CIPHER_CTX)); - return c; } static int seal_gc(lua_State *L) { - seal_context *c = (seal_context *)luaL_checkudata(L, 1, LUACRYPTO_SEALNAME); - EVP_CIPHER_CTX_cleanup(c->ctx); - free(c->ctx); + seal_context *c = seal_get_at(L, 1); + int n = evp_cipher_ctx_cleanup(L, &c->ctx); + if(n) return n; + if (c->ek != NULL) { free(c->ek); @@ -1315,9 +1692,9 @@ static int seal_gc(lua_State *L) static int seal_tostring(lua_State *L) { - seal_context *c = (seal_context *)luaL_checkudata(L, 1, LUACRYPTO_SEALNAME); + seal_context *c = seal_get_at(L, 1); char s[64]; - sprintf(s, "%s %p %s", LUACRYPTO_SEALNAME, (void *)c, EVP_CIPHER_name(c->ctx->cipher)); + sprintf(s, "%s %p %s", LUACRYPTO_SEALNAME, (void *)c, EVP_CIPHER_name(c->ctx.ctx->cipher)); lua_pushstring(L, s); return 1; } @@ -1336,11 +1713,11 @@ static int seal_fnew(lua_State *L) pkey = (EVP_PKEY **)luaL_checkudata(L, 2, LUACRYPTO_PKEYNAME); seal_ctx = seal_pnew(L); - EVP_CIPHER_CTX_init(seal_ctx->ctx); + EVP_CIPHER_CTX_init(seal_ctx->ctx.ctx); seal_ctx->ek = (unsigned char *)malloc((size_t)EVP_PKEY_size(*pkey) * (size_t)npubk); - if (!EVP_SealInit(seal_ctx->ctx, cipher, &seal_ctx->ek, &seal_ctx->eklen, + if (!EVP_SealInit(seal_ctx->ctx.ctx, cipher, &seal_ctx->ek, &seal_ctx->eklen, seal_ctx->iv, pkey, npubk)) { free(seal_ctx->ek); @@ -1351,37 +1728,42 @@ static int seal_fnew(lua_State *L) return 1; } -static int seal_update(lua_State *L) +static int seal_set_writer(lua_State *L) { - seal_context *c = (seal_context *)luaL_checkudata(L, 1, LUACRYPTO_SEALNAME); - size_t input_len = 0; - const unsigned char *input = (unsigned char *)luaL_checklstring(L, 2, &input_len); - int output_len = 0; + seal_context *c = seal_get_at(L, 1); + return evp_cipher_ctx_set_writer(L, &c->ctx); +} - unsigned char *temp = (unsigned char *)malloc(input_len + (size_t)EVP_CIPHER_CTX_block_size(c->ctx)); - EVP_SealUpdate(c->ctx, temp, &output_len, input, (int)input_len); - lua_pushlstring(L, (char *)temp, (size_t)output_len); - free(temp); +static int seal_get_writer(lua_State *L) +{ + seal_context *c = seal_get_at(L, 1); + return evp_cipher_ctx_get_writer(L, &c->ctx); +} - return 1; +static int seal_update(lua_State *L) +{ + seal_context *c = seal_get_at(L, 1); + size_t input_len; + const char *input = correct_range(L, &input_len); + return evp_cipher_ctx_update(L, &c->ctx, EVP_EncryptUpdate, input, input_len); } static int seal_final(lua_State *L) { - seal_context *c = (seal_context *)luaL_checkudata(L, 1, LUACRYPTO_SEALNAME); - int output_len = 0; - unsigned char buffer[EVP_MAX_BLOCK_LENGTH]; - - EVP_SealFinal(c->ctx, buffer, &output_len); - lua_pushlstring(L, (char *)buffer, (size_t)output_len); + seal_context *c = seal_get_at(L, 1); + int ret = evp_cipher_ctx_final(L, &c->ctx, EVP_SealFinal); + if (ret > 1) //@fixme detect if crypto_error change interface + { + return ret; + } lua_pushlstring(L, (const char *)c->ek, (size_t)c->eklen); - lua_pushlstring(L, (const char *)c->iv, (size_t)EVP_CIPHER_iv_length(c->ctx->cipher)); + lua_pushlstring(L, (const char *)c->iv, (size_t)EVP_CIPHER_iv_length(c->ctx.ctx->cipher)); free(c->ek); c->ek = NULL; - return 3; + return ret + 2; } static int seal_fseal(lua_State *L) @@ -1464,23 +1846,31 @@ static int seal_fseal(lua_State *L) return 3; } +//} + /*************** OPEN API ***************/ +//{ typedef struct open_context { - EVP_CIPHER_CTX *ctx; + l_evp_cipher_ctx ctx; EVP_CIPHER *cipher; int pkey_ref; } open_context; +static open_context *open_get_at(lua_State *L, int i) +{ + open_context *c = (open_context *)luaL_checkudata(L, i, LUACRYPTO_OPENNAME); + return c; +} + static open_context *open_pnew(lua_State *L) { open_context *c = (open_context *)lua_newuserdata(L, sizeof(open_context)); luaL_getmetatable(L, LUACRYPTO_OPENNAME); lua_setmetatable(L, -2); - memset(c, 0, sizeof(open_context)); - c->ctx = (EVP_CIPHER_CTX *)malloc(sizeof(EVP_CIPHER_CTX)); + c->ctx.cb_ref = c->ctx.ud_ref = LUA_NOREF; c->pkey_ref = LUA_NOREF; return c; @@ -1488,9 +1878,10 @@ static open_context *open_pnew(lua_State *L) static int open_gc(lua_State *L) { - open_context *c = luaL_checkudata(L, 1, LUACRYPTO_OPENNAME); - EVP_CIPHER_CTX_cleanup(c->ctx); - free(c->ctx); + open_context *c = open_get_at(L, 1); + int n = evp_cipher_ctx_cleanup(L, &c->ctx); + if(n) return n; + if (c->pkey_ref != LUA_NOREF) { luaL_unref(L, LUA_REGISTRYINDEX, c->pkey_ref); @@ -1500,7 +1891,7 @@ static int open_gc(lua_State *L) static int open_tostring(lua_State *L) { - open_context *c = (open_context *)luaL_checkudata(L, 1, LUACRYPTO_OPENNAME); + open_context *c = open_get_at(L, 1); EVP_PKEY **pkey = (EVP_PKEY **)luaL_checkudata(L, -1, LUACRYPTO_PKEYNAME); char s[64]; @@ -1536,10 +1927,10 @@ static int open_fnew(lua_State *L) return luaL_argerror(L, 4, "invalid iv length"); open_ctx = open_pnew(L); - EVP_CIPHER_CTX_init(open_ctx->ctx); + EVP_CIPHER_CTX_init(open_ctx->ctx.ctx); open_ctx->cipher = (EVP_CIPHER *)cipher; - if (!EVP_OpenInit(open_ctx->ctx, open_ctx->cipher, encrypted_key, + if (!EVP_OpenInit(open_ctx->ctx.ctx, open_ctx->cipher, encrypted_key, (int)lua_objlen(L, 3), iv, *pkey)) return crypto_error(L); @@ -1549,44 +1940,36 @@ static int open_fnew(lua_State *L) return 1; } -static int open_update(lua_State *L) +static int open_set_writer(lua_State *L) { - open_context *c = (open_context *)luaL_checkudata(L, 1, LUACRYPTO_OPENNAME); - size_t input_len = 0; - const unsigned char *input = (unsigned char *)luaL_checklstring(L, 2, &input_len); - - luaL_Buffer buffer; - luaL_buffinit(L, &buffer); - - while (input_len > 0) - { - int output_length; - unsigned char *temp = (unsigned char *)luaL_prepbuffer(&buffer); - size_t sz = MIN(LUAL_BUFFERSIZE - 1, input_len); - if (!EVP_OpenUpdate(c->ctx, temp, &output_length, input, (int)sz)) - { - return crypto_error(L); - } + open_context *c = open_get_at(L, 1); + return evp_cipher_ctx_set_writer(L, &c->ctx); +} - input += sz; - input_len -= sz; - luaL_addsize(&buffer, output_length); - } +static int open_get_writer(lua_State *L) +{ + open_context *c = open_get_at(L, 1); + return evp_cipher_ctx_get_writer(L, &c->ctx); +} - luaL_pushresult(&buffer); - return 1; +static int open_update(lua_State *L) +{ + open_context *c = open_get_at(L, 1); + size_t input_len; + const char *input = correct_range(L, &input_len); + return evp_cipher_ctx_update(L, &c->ctx, EVP_DecryptUpdate, input, input_len); } static int open_final(lua_State *L) { - open_context *c = (open_context *)luaL_checkudata(L, 1, LUACRYPTO_OPENNAME); - int output_len = 0; - unsigned char buffer[EVP_MAX_BLOCK_LENGTH]; - - EVP_OpenFinal(c->ctx, buffer, &output_len); - lua_pushlstring(L, (char *)buffer, (size_t)output_len); - - return 1; + open_context *c = open_get_at(L, 1); + int ret = evp_cipher_ctx_final(L, &c->ctx, EVP_OpenFinal); + if (ret == 0) + { + lua_settop(L, 1); + return 1; + } + return ret; } static int open_fopen(lua_State *L) @@ -1665,7 +2048,34 @@ static int open_fopen(lua_State *L) return 1; } +//} + /*************** CORE API ***************/ +//{ + +static int luacrypto_openssl_version(lua_State *L) +{ + lua_pushstring(L, OPENSSL_VERSION_TEXT); + return 1; +} + +static int luacrypto_openssl_nversion(lua_State *L) +{ + lua_pushnumber(L, OPENSSL_VERSION_NUMBER); + return 1; +} + +static int luacrypto_shlib_version(lua_State *L) +{ + lua_pushstring(L, SHLIB_VERSION_NUMBER); + return 1; +} + +static int luacrypto_shlib_history(lua_State *L) +{ + lua_pushstring(L, SHLIB_VERSION_HISTORY); + return 1; +} static void list_callback(const OBJ_NAME *obj, void *arg) { @@ -1699,7 +2109,10 @@ static int luacrypto_hex(lua_State *L) return 1; } +//} + /*************** x509 API ***************/ +//{ static X509 *x509__load_cert(BIO *cert) { @@ -1894,6 +2307,9 @@ static int x509_ca_add_pem(lua_State *L) return 1; } +//} + + /* ** Create a metatable and leave it on top of the stack. */ @@ -1942,6 +2358,32 @@ static void create_call_table(lua_State *L, const char *name, lua_CFunction crea {NULL, NULL}, \ } +#define EVP_METHODS_EX(name) \ + struct luaL_Reg name##_methods[] = { \ + { "__tostring", name##_tostring }, \ + { "__gc", name##_gc }, \ + { "final", name##_final }, \ + { "tostring", name##_tostring }, \ + { "update", name##_update }, \ + { "clone", name##_clone }, \ + { "resetiv", name##_resetiv }, \ + { "set_writer", name##_set_writer },\ + { "get_writer", name##_get_writer },\ + {NULL, NULL}, \ + } + +#define EVP_METHODS_SEAL(name) \ + struct luaL_Reg name##_methods[] = { \ + { "__tostring", name##_tostring }, \ + { "__gc", name##_gc }, \ + { "final", name##_final }, \ + { "tostring", name##_tostring }, \ + { "update", name##_update }, \ + { "set_writer", name##_set_writer },\ + { "get_writer", name##_get_writer },\ + {NULL, NULL}, \ + } + /* ** Create metatables for each class of object. */ @@ -1950,6 +2392,11 @@ static void create_metatables (lua_State *L) int top; struct luaL_Reg core_functions[] = { + { "openssl_version", luacrypto_openssl_version }, + { "openssl_version_number", luacrypto_openssl_nversion }, + { "openssl_shlib_version", luacrypto_shlib_version }, + { "openssl_shlib_history", luacrypto_shlib_history }, + { "list", luacrypto_list }, { "hex", luacrypto_hex }, { NULL, NULL } @@ -1965,12 +2412,12 @@ static void create_metatables (lua_State *L) { "clone", digest_clone }, {NULL, NULL} }; - EVP_METHODS(encrypt); - EVP_METHODS(decrypt); + EVP_METHODS_EX(encrypt); + EVP_METHODS_EX(decrypt); EVP_METHODS(sign); EVP_METHODS(verify); - EVP_METHODS(seal); - EVP_METHODS(open); + EVP_METHODS_SEAL(seal); + EVP_METHODS_SEAL(open); struct luaL_Reg hmac_functions[] = { { "digest", hmac_fdigest }, diff --git a/tests/encrypt.lua b/tests/encrypt.lua index 5182373..0237855 100644 --- a/tests/encrypt.lua +++ b/tests/encrypt.lua @@ -39,6 +39,21 @@ local p2 = ctx:final() local res3 = p1 .. p2 assert(res == res3, "constructed result is different from direct") +do -- test writer +local t = {} +local ctx = crypto.encrypt.new(cipher, key, iv) +ctx:set_writer(table.insert, t) + +local a,b = ctx:get_writer() +assert(a == table.insert) +assert(b == t) + +assert(ctx == ctx:update(text)) +assert(ctx == ctx:final()) +local res3 = table.concat(t) +assert(res == res3, "constructed result is different from direct") +end + -- TESTING DECRYPT assert(crypto.decrypt, "missing crypto.decrypt") @@ -57,6 +72,21 @@ local dec2 = p1 .. p2 assert(dec2 == text, "different partial result") +do -- test writer +local t = {} +local ctx = crypto.decrypt.new(cipher, key, iv) +ctx:set_writer(table.insert, t) + +local a,b = ctx:get_writer() +assert(a == table.insert) +assert(b == t) + +assert(ctx == ctx:update(res)) +assert(ctx == ctx:final()) +local dec2 = table.concat(t) +assert(dec2 == text, "different partial result") +end + -- Testing errors when decrypting local ctx, err = crypto.decrypt("aes128", res, key.."improper key", iv) assert(not ctx and err, "should have failed") diff --git a/tests/encrypt_large.lua b/tests/encrypt_large.lua new file mode 100644 index 0000000..ccdca0c --- /dev/null +++ b/tests/encrypt_large.lua @@ -0,0 +1,47 @@ +crypto = require 'crypto' + +local cipher = 'aes128' +local text = ('0124'):rep(3):rep(1333) .. 'abcd' +local key = 'abcd' +local iv = '1234' + +local res = assert(crypto.encrypt(cipher, text, key, iv)) +assert(type(res) == "string", "wrong result type, expecting string") +assert(#res % 16 == 0, "unexpected result size") -- aes128 block size is 16 bytes + +do -- encrypt large block +local ctx = crypto.encrypt.new(cipher, key, iv) +local p1 = ctx:update(text) +local p2 = ctx:final() +assert(res == p1..p2, "constructed result is different from direct") +end + +do -- encrypt large block with writer +local ctx = crypto.encrypt.new(cipher, key, iv) +local t = {} +ctx:set_writer(table.insert, t) +assert(ctx == ctx:update(text)) +assert(ctx == ctx:final()) +assert(res == table.concat(t), "constructed result is different from direct") +end + +do -- decrypt +local text2 = assert(crypto.decrypt(cipher, res, key, iv)) +assert(text2 == text) +end + +do -- decrypt large block +local ctx = crypto.decrypt.new(cipher, key, iv) +local p1 = ctx:update(res) +local p2 = ctx:final() +assert(text == p1..p2, "constructed result is different from direct") +end + +do -- decrypt large block with writer +local ctx = crypto.decrypt.new(cipher, key, iv) +local t = {} +ctx:set_writer(table.insert, t) +assert(ctx == ctx:update(res)) +assert(ctx == ctx:final()) +assert(text == table.concat(t), "constructed result is different from direct") +end diff --git a/tests/open_seal.lua b/tests/open_seal.lua index 1bc12de..7dd81e2 100644 --- a/tests/open_seal.lua +++ b/tests/open_seal.lua @@ -1,3 +1,13 @@ +local function ichunks(len, chunk_size) + return function(_, b) + b = b + chunk_size + if b > len then return nil end + local e = b + chunk_size - 1 + if e > len then e = len end + return b, e + end, nil, -chunk_size + 1 +end + crypto = require 'crypto' assert(crypto.pkey, "crypto.pkey is unavaliable") @@ -18,7 +28,7 @@ message = string.rep('This message will be signed', 122) data, ek, iv = assert(crypto.seal("aes128", message, kpub)) assert(crypto.open("aes128", data, kpriv, ek, iv) == message) - +do local ctx = crypto.seal.new("aes128", kpub) local p1 = ctx:update(message) local p2, ek_2, iv_2 = ctx:final() @@ -28,5 +38,49 @@ local ctx = crypto.open.new("aes128", kpriv, ek_2, iv_2) p3 = ctx:update(p1..p2) p4 = ctx:final() assert(message == (p3 .. p4)) +end + +do +local ctx = crypto.seal.new("aes128", kpub) +local p1 = '' +for b,e in ichunks(#message, 25) do + p1 = p1 .. ctx:update(message, b, e) +end +local p2, ek_2, iv_2 = ctx:final() + +assert(crypto.open("aes128", p1..p2, kpriv, ek_2, iv_2) == message) + +local ctx = crypto.open.new("aes128", kpriv, ek_2, iv_2) + +p3 = ctx:update(p1..p2, 1, #p1) +p3 = p3 .. ctx:update(p1..p2, #p1+1, #(p1..p2)) +p4 = ctx:final() +assert(message == (p3 .. p4)) +end + +do +local ctx = crypto.seal.new("aes128", kpub) +local t = {} +ctx:set_writer(table.insert, t) +local a, b = ctx:get_writer() +assert(a == table.insert) +assert(b == t) +assert(ctx == ctx:update(message)) +local ek_2, iv_2 = ctx:final() + +assert(crypto.open("aes128", table.concat(t), kpriv, ek_2, iv_2) == message) + +local ctx = crypto.open.new("aes128", kpriv, ek_2, iv_2) +local t2 = {} +ctx:set_writer(table.insert, t2) +local a, b = ctx:get_writer() +assert(a == table.insert) +assert(b == t2) + +assert(ctx == ctx:update(table.concat(t))) +assert(ctx == ctx:final()) + +assert(message == table.concat(t2)) +end print("OK") diff --git a/tests/resetiv.lua b/tests/resetiv.lua new file mode 100644 index 0000000..7a950e4 --- /dev/null +++ b/tests/resetiv.lua @@ -0,0 +1,51 @@ +-- example of AES-256-CTR with custom counter + +local function ichunks(len, chunk_size) + return function(_, b) + b = b + chunk_size + if b > len then return nil end + local e = b + chunk_size - 1 + if e > len then e = len end + return b, e + end, nil, -chunk_size + 1 +end + +local crypto = require "crypto" + +local msg = ("012"):rep(3):rep(1333) .. 'abcd' +local KEY = {0x04, 0xF9, 0x4A, 0xFB, 0x60, 0xAF, 0x47, 0x44, 0xD4, 0xDB, 0x9B, 0x3A, 0xE7, 0x23, 0x3E, 0xC6} +local IV = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} +local block_size = 16 + +-- custom IV counter +-- compatible with WinZip +local function inciv(iv) + for k, v in ipairs(iv) do + if iv == 255 then iv[k] = 0 + else iv[k] = v + 1 return iv end + end +end + +local ctx = crypto.encrypt.new('aes-256-ctr', KEY) + +local t1 = {} ctx:set_writer(table.insert, t1) + +local t2 = {} + +for b, e in ichunks(#msg, block_size) do + -- use same ctx, do not call msg:sub and use writer + ctx:resetiv(inciv(IV)) + ctx:update(msg, b, e) + + -- each time call EVP Init/Update/Final + local chunk = crypto.encrypt('aes-256-ctr', msg:sub(b, e), KEY, IV) + table.insert(t2, chunk) + + assert(t2[#t2] == t1[#t1]) +end + +assert(ctx == ctx:final()) + +assert(table.concat(t1) == table.concat(t2)) + +print("OK") \ No newline at end of file diff --git a/tests/unit/digest.lua b/tests/unit/digest.lua new file mode 100644 index 0000000..c4d142d --- /dev/null +++ b/tests/unit/digest.lua @@ -0,0 +1,118 @@ +--[[ +-- $Id: test.lua,v 1.3 2006/08/25 03:24:17 nezroy Exp $ +-- See Copyright Notice in license.html +--]] + +local lunit = require "lunit" +local TEST_CASE = lunit.TEST_CASE +local crypto = require("crypto") + +local hmac_key = "luacrypto" + +local function make_test(t, st, msg, KNOWN) + + local _ENV = TEST_CASE ("digest." .. t .. "." .. st) + + local etalon = KNOWN[t] + + local d + + local pos + + local function read(n) + if n == "*all" then return msg end + if pos > #msg then return nil end + local i = pos + pos = pos + n + return (msg:sub(i, pos - 1)) + end + + function setup() + pos = 1 + if (t == "hmac") then + d = assert_userdata(crypto.hmac.new("sha1", hmac_key)) + else + d = assert_userdata(crypto.digest.new(t)) + end + end + + function teardown() + pos = nil + d = nil + end + + function test_all() + local msg = read("*all") + assert_equal(etalon, d:final(msg), "all") + end + + function test_loop() + while true do + local c = read(1) + if c == nil then break end + d:update(c) + end + assert_equal(etalon, d:final(), "loop") + assert_equal(etalon, d:final(), "again") + end + + function test_digest() + local msg = read("*all") + local h + if (t ~= "hmac") then + h = assert_string(crypto.digest(t, msg)) + else + h = assert_string(crypto.hmac.digest("sha1", msg, hmac_key)) + end + assert_equal(etalon, h, "alone") + end + + function test_reset() + d:update("hello") + d:reset() + + while true do + local c = read(math.random(1, 16)) + if c == nil then break end + d:update(c) + end + assert_equal(etalon, d:final(), "reset") + end + + function test_clone() + local d2 + while true do + local c = read(1) + if c == nil then break end + d:update(c) + if not d2 then d2 = d:clone() else d2:update(c) end + end + assert_equal(etalon, d:final(), "clone") + assert_equal(etalon, d2:final(), "clone") + end + +end + +local msg = "This is a sample message to use for hashing tests.\n" +local KNOWN = { + md5 = "09920f6f666f8e7b09a8d00bd4d06873"; + sha1 = "d6ed6e26ebeb37ba0792ec75a3d0b4dcec279d25"; + hmac = "70a7ea81a287d094c534cdd67be82e85066e13be"; +} + +for i, t in ipairs({"sha1", "md5", "hmac"}) do + make_test(t, "origin", msg, KNOWN) +end + +local msg = ("01"):rep(3):rep(1333) .. 'abcd' +local KNOWN = { + sha1 = "adc089981eafcc442be904dc6dcbd488ef659c92"; + md5 = "f4432f3bdbcb4c9f590cefd5aee17ef9"; + hmac = "d91d232feecf25724fad0713bd0c5287863cc8bb"; +} + +for i, t in ipairs({"sha1", "md5", "hmac"}) do + make_test(t, "large", msg, KNOWN) +end + +if not _TEST then lunit.run() end \ No newline at end of file diff --git a/tests/unit/encrypt.lua b/tests/unit/encrypt.lua new file mode 100644 index 0000000..aa362c4 --- /dev/null +++ b/tests/unit/encrypt.lua @@ -0,0 +1,177 @@ +function tohex(s) + return (s:gsub('.', function (c) return string.format("%02x", string.byte(c)) end)) +end +function hexprint(s) + print(crypto.hex(s)) +end + +local lunit = require "lunit" +local TEST_CASE = lunit.TEST_CASE +local crypto = require 'crypto' + +local FAIL = function(str) return function() lunit.fail(str) end end + +local _ENV = TEST_CASE "TESTING HEX" + +function test() + assert_function(crypto.hex, "missing crypto.hex") + local tst = 'abcd' + local actual = crypto.hex(tst) + local expected = tohex(tst) + assert_equal(expected, actual, "different hex results") +end + +local _ENV = TEST_CASE "TESTING ENCRYPT/DECRYPT" +if not crypto.encrypt then test = FAIL"missing crypto.encrypt" +elseif not crypto.decrypt then test = FAIL"missing crypto.decrypt" +else + +local cipher = 'aes128' +local text = 'Hello world!' +local key = 'abcd' +local iv = '1234' +local res + +function setup() + res = assert_string(crypto.encrypt(cipher, text, key, iv)) + assert_equal(0, #res % 16, "unexpected result size") -- aes128 block size is 16bytes + assert_equal("9bac9a71dd600824706096852e7282df", crypto.hex(res), "unexpected result") +end + +function test_encrypt() + local res2 = assert_string(crypto.encrypt(cipher, text, key, iv)) + assert_equal(res, res2, "the results are different!") +end + +function test_encrypt_update() + assert_function(crypto.encrypt.new, "missing crypto.encrypt.new") + local ctx = assert_userdata(crypto.encrypt.new(cipher, key, iv)) + local p1 = assert_string(ctx:update(text)) + local p2 = assert_string(ctx:final()) + local res3 = p1 .. p2 + assert_equal(res, res3, "constructed result is different from direct") +end + +function test_encrypt_writer() + assert_function(crypto.encrypt.new, "missing crypto.encrypt.new") + local t = {} + local ctx = assert_userdata(crypto.encrypt.new(cipher, key, iv)) + ctx:set_writer(table.insert, t) + + local a, b = ctx:get_writer() + assert_equal(table.insert, a, b) + assert_equal(t, b) + + assert_equal(ctx, ctx:update(text)) + assert_equal(ctx, ctx:final()) + + local res3 = table.concat(t) + assert_equal(res, res3, "constructed result is different from direct") +end + +function test_decrypt() + local dec = assert_string(crypto.decrypt(cipher, res, key, iv)) + assert_equal(text, dec, "different direct result") +end + +function test_decrypt_update() + assert_function(crypto.decrypt.new, "missing crypto.decrypt.new") + + local ctx = assert_userdata(crypto.decrypt.new(cipher, key, iv)) + local p1 = assert_string(ctx:update(res)) + local p2 = assert_string(ctx:final()) + local dec2 = p1 .. p2 + + assert_equal(text, dec2, "different partial result") +end + +function test_decrypt_writer() + local t = {} + local ctx = assert_userdata(crypto.decrypt.new(cipher, key, iv)) + ctx:set_writer(table.insert, t) + + local a,b = ctx:get_writer() + assert_equal(table.insert, a, b) + assert_equal(t, b) + + assert_equal(ctx, ctx:update(res)) + assert_equal(ctx, ctx:final()) + local dec2 = table.concat(t) + assert_equal(text, dec2, "different partial result") +end + +function test_decrypt_error_key() + -- Testing errors when decrypting + local ctx, err = assert_nil(crypto.decrypt("aes128", res, key.."improper key", iv)) + assert_not_nil(err, "should have failed") +end + +function test_decrypt_invalid_iv() + -- wrong iv, will result in garbage + local dec, err = assert_string(crypto.decrypt("aes128", res, key, iv .. "foo")) + assert_not_equal(text, dec, "should have failed") +end + +function test_decrypt_invalid_data() + local dec, err = assert_nil(crypto.decrypt("aes128", res .. "foo", key, iv)) + assert_not_nil(err, "should have failed") +end + +function test_decrypt_error_iv() + -- don't crash on an invalid iv + local ok, dec, err = pcall(crypto.decrypt, "aes128", res, key, iv .. "123456123456123456") + assert_false(ok, "should have failed") + assert_not_nil(dec, "should have failed") +end + +function test_decrypt_new_error_iv() + local ok, ctx = pcall(crypto.decrypt.new, "aes128", key, iv .. "123456123456123456") + assert_false(ok, "should have failed") + assert_not_nil(ctx, "should have failed") +end + +function test_decrypt_error_large_iv() + local ok, dec, err = pcall(crypto.decrypt, "aes128", res, string.rep(key, 100), iv) + assert_false(ok, "should have failed") + assert_not_nil(dec, "should have failed") +end + +function test_decrypt_new_error_large_iv() + local ok, ctx = pcall(crypto.decrypt.new, "aes128", string.rep(key, 100), iv) + assert_false(ok, "should have failed") + assert_not_nil(ctx, "should have failed") +end + +function test_encrypt_error_iv() + -- don't crash on an invalid iv + local ok, dec, err = pcall(crypto.encrypt, "aes128", res, key, iv .. "123456123456123456") + assert_false(ok, "should have failed") + assert_not_nil(dec, "should have failed") +end + +function test_encrypt_new_error_iv() + local ok, ctx = pcall(crypto.encrypt.new, "aes128", key, iv .. "123456123456123456") + assert_false(ok, "should have failed") + assert_not_nil(ctx, "should have failed") +end + +function test_encrypt_error_large_iv() + local ok, dec, err = pcall(crypto.encrypt, "aes128", res, string.rep(key, 100), iv) + assert_false(ok, "should have failed") + assert_not_nil(dec, "should have failed") +end + +function test_encrypt_new_error_large_iv() + local ok, ctx = pcall(crypto.encrypt.new, "aes128", string.rep(key, 100), iv) + assert_false(ok, "should have failed") + assert_not_nil(ctx, "should have failed") +end + +function test_empty_cycle() + local res = crypto.decrypt("aes128", crypto.encrypt("aes128", "", key, iv), key, iv) + assert_equal("", res) +end + +end + +if not _TEST then lunit.run() end \ No newline at end of file diff --git a/tests/unit/encrypt_large.lua b/tests/unit/encrypt_large.lua new file mode 100644 index 0000000..74ca3d0 --- /dev/null +++ b/tests/unit/encrypt_large.lua @@ -0,0 +1,70 @@ +local lunit = require "lunit" +local TEST_CASE = lunit.TEST_CASE +local crypto = require 'crypto' + +local cipher = 'aes128' +local key = 'abcd' +local iv = '1234' + +local function make_test(N) + +local text = ('0124'):rep(3):rep(N) .. 'abcd' + +local _ENV = TEST_CASE("encrypt.large." .. tostring(N)) + +local res + +function setup() + assert(crypto.encrypt) + res = assert_string(crypto.encrypt(cipher, text, key, iv)) + assert_equal(0, #res % 16, "unexpected result size") -- aes128 block size is 16 bytes +end + +function teardown() + res = nil +end + +function test_decrypt() + local text2 = assert_string(crypto.decrypt(cipher, res, key, iv)) + assert_equal(text, text2) +end + +function test_encrypt_update() + local ctx = assert_userdata(crypto.encrypt.new(cipher, key, iv)) + local p1 = assert_string(ctx:update(text)) + local p2 = assert_string(ctx:final()) + assert_equal(res, p1..p2, "constructed result is different from direct") +end + +function test_encrypt_writer() + local ctx = assert_userdata(crypto.encrypt.new(cipher, key, iv)) + local t = {} ctx:set_writer(table.insert, t) + assert_equal(ctx, ctx:update(text)) + assert_equal(ctx, ctx:final()) + assert_equal(res, table.concat(t), "constructed result is different from direct") +end + +function test_decrypt_update() + local ctx = assert_userdata(crypto.decrypt.new(cipher, key, iv)) + local p1 = assert_string(ctx:update(res)) + local p2 = assert_string(ctx:final()) + assert_equal(text, p1..p2, "constructed result is different from direct") +end + +function test_decrypt_writer() + local ctx = assert_userdata(crypto.decrypt.new(cipher, key, iv)) + local t = {} ctx:set_writer(table.insert, t) + assert_equal(ctx, ctx:update(res)) + assert_equal(ctx, ctx:final()) + assert_equal(text, table.concat(t), "constructed result is different from direct") +end + +end + +make_test(1) + +make_test(16) + +make_test(1333) + +if not _TEST then lunit.run() end \ No newline at end of file diff --git a/tests/unit/open_seal.lua b/tests/unit/open_seal.lua new file mode 100644 index 0000000..3908ded --- /dev/null +++ b/tests/unit/open_seal.lua @@ -0,0 +1,105 @@ +local function ichunks(len, chunk_size) + return function(_, b) + b = b + chunk_size + if b > len then return nil end + local e = b + chunk_size - 1 + if e > len then e = len end + return b, e + end, nil, -chunk_size + 1 +end + +local lunit = require "lunit" +local TEST_CASE = lunit.TEST_CASE +local crypto = require 'crypto' + +local FAIL = function(str) return function() lunit.fail(str) end end + +local _ENV = TEST_CASE"open/seal" + +if not crypto.pkey then test = FAIL"crypto.pkey is unavaliable" +elseif not crypto.open then test = FAIL"crypto.open is unavaliable" +elseif not crypto.seal then test = FAIL"crypto.seal is unavaliable" +else + +local message = string.rep('This message will be signed', 122) +local kpub, kpriv + +function setup() + local k = assert_userdata(crypto.pkey.generate('rsa', 1024), "no key generated") + k:write('pub.pem', 'priv.pem') + kpub = assert_userdata(crypto.pkey.read('pub.pem')) + kpriv = assert_userdata(crypto.pkey.read('priv.pem', true)) +end + +function teardown() + os.remove('pub.pem') + os.remove('priv.pem') +end + +function test_cycle() + local data, ek, iv = assert_string(crypto.seal("aes128", message, kpub)) + assert_equal(message, crypto.open("aes128", data, kpriv, ek, iv)) +end + +function test_cycle_update() + local ctx = assert_userdata(crypto.seal.new("aes128", kpub)) + local p1 = assert_string(ctx:update(message)) + local p2, ek_2, iv_2 = assert_string(ctx:final()) + assert_string(ek_2) + assert_string(iv_2) + + assert_equal(message, crypto.open("aes128", p1..p2, kpriv, ek_2, iv_2)) + + local ctx = assert_userdata(crypto.open.new("aes128", kpriv, ek_2, iv_2)) + p3 = assert_string(ctx:update(p1..p2)) + p4 = assert_string(ctx:final()) + + assert_equal(message, (p3 .. p4)) +end + +function test_cycle_update_by_range() + local ctx = assert_userdata(crypto.seal.new("aes128", kpub)) + local p1 = '' + for b,e in ichunks(#message, 25) do + p1 = p1 .. assert_string(ctx:update(message, b, e)) + end + local p2, ek_2, iv_2 = assert_string(ctx:final()) + + assert_equal(message, crypto.open("aes128", p1..p2, kpriv, ek_2, iv_2)) + + local ctx = assert_userdata(crypto.open.new("aes128", kpriv, ek_2, iv_2)) + + local p3 = assert_string(ctx:update(p1..p2, 1, #p1)) + p3 = p3 .. assert_string(ctx:update(p1..p2, #p1+1, #(p1..p2))) + local p4 = assert_string(ctx:final()) + assert_equal(message, (p3 .. p4)) +end + +function test_cycle_writer() + local ctx = assert_userdata(crypto.seal.new("aes128", kpub)) + local t = {} + ctx:set_writer(table.insert, t) + local a, b = ctx:get_writer() + assert_equal(table.insert, a, b) + assert_equal(t, b) + assert_equal(ctx, ctx:update(message)) + local ek_2, iv_2 = assert_string(ctx:final()) + + assert_equal(message, crypto.open("aes128", table.concat(t), kpriv, ek_2, iv_2)) + + local ctx = assert_userdata(crypto.open.new("aes128", kpriv, ek_2, iv_2)) + local t2 = {} + ctx:set_writer(table.insert, t2) + local a, b = ctx:get_writer() + assert_equal(table.insert, a, b) + assert_equal(t2, b) + + assert_equal(ctx, ctx:update(table.concat(t))) + assert_equal(ctx, ctx:final()) + + assert_equal(message, table.concat(t2)) +end + +end + +if not _TEST then lunit.run() end \ No newline at end of file diff --git a/tests/unit/pkeytest.lua b/tests/unit/pkeytest.lua new file mode 100644 index 0000000..cdbc0c1 --- /dev/null +++ b/tests/unit/pkeytest.lua @@ -0,0 +1,83 @@ + +local lunit = require "lunit" +local TEST_CASE = lunit.TEST_CASE +local crypto = require 'crypto' + +local FAIL = function(str) return function() lunit.fail(str) end end + +local _ENV = TEST_CASE"pkey" +if not crypto.pkey then test = FAIL"crypto.pkey is unavaliable" +elseif not crypto.sign then test = FAIL"crypto.sign is unavaliable" +elseif not crypto.verify then test = FAIL"crypto.verify is unavaliable" +else + +local function test_verify(kpub, kpriv) + local message = 'This message will be signed' + + local sig = assert_string(crypto.sign('md5', message, kpriv)) + local verified = crypto.verify('md5', message, sig, kpub) + assert_true(verified, "message not verified") + + local nverified = crypto.verify('md5', message..'x', sig, kpub) + assert_false(nverified, "message verified, when it shouldn't be") +end + +local RSA_PUBLIC_KEY = [[ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAIy7sjJXo3ePWF1PZ81DelW1E +4VphWD+VPBzT8oCYY9dViJ4lszW/t5LX0IYAm+veuJyF5ffkAeeOWvI7vCg+5s3b +l9QqXgU8izuiXD0W6Wfm0YUU9VLGiFWnyTHpvZwqhnqmSEFCqPh+bWshCn/J5pZa +g8GOfgG42UgCrxnNWwIDAQAB +-----END PUBLIC KEY----- +]] + +local RSA_PRIV_KEY = [[ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQDAIy7sjJXo3ePWF1PZ81DelW1E4VphWD+VPBzT8oCYY9dViJ4l +szW/t5LX0IYAm+veuJyF5ffkAeeOWvI7vCg+5s3bl9QqXgU8izuiXD0W6Wfm0YUU +9VLGiFWnyTHpvZwqhnqmSEFCqPh+bWshCn/J5pZag8GOfgG42UgCrxnNWwIDAQAB +AoGAa2R++trNg75adbTOOnlEj1ToIWLwWI6x42EZH+JgvEy59GYLNzlG5qTd3+D+ +tWJxYSjA3BqhBwGFgs0UrgzKVPwKbj1nbX0w91PmfdyGEutN84xRtZWkdMBiFacV +Hy8Y0rvw/xmlf39xkv1n8whtb7sKxZjxRwVWpSU2i5ovQ5kCQQD98agvwLRoJQ3e +AkuIpSNHfk9lRkr2A0ZHJjRRYOWN+xl/bShxMKCSrlzHqmIEd8wIkgXkWFSCDO4M +WcE3G2y3AkEAwbFr6SFQHqh48hO8Lq040S8y+wVZrH7DIwYM3Ckc7JnurFQP9B6U +2BOPsLuCNoWeMJOwyJiIXwd4KT7XvzAIfQJAbNAJ0zxtkVqfUIwHNawdK9tRxgGS +yUup537VWDF+65G24UUy2R2PEIsqMlwt1+BFSz7Wy3uV6owDzMMA6c4UjQJAJC/V +jVSf91paXj+5pK7QMqSyzZsOSd/U7TIwLOGxebK4mJGL+XvNKyFccxRVG4KTL1go +axG0SKzIkkwfWqTKsQJAf58QgbmGIwDwQgk2StWuulY9HhGpd73JySPyTKR2Lmpe +wDJiqtOCnY3hEss2co97U/vzL+Cic4hXT3gGAQiDwQ== +-----END RSA PRIVATE KEY----- +]] + +function setup() +end + +function teardown() + os.remove('pub.pem') + os.remove('priv.pem') +end + +function test_predefine() + local kpub = assert_userdata(crypto.pkey.from_pem(RSA_PUBLIC_KEY)) + local kpriv = assert_userdata(crypto.pkey.from_pem(RSA_PRIV_KEY, true)) + + assert_equal(RSA_PUBLIC_KEY, kpub:to_pem()) + assert_equal(RSA_PRIV_KEY, kpriv:to_pem(true)) + + test_verify(kpub, kpriv) +end + +function test_generate() + local k = crypto.pkey.generate('rsa', 1024) + assert(k, "no key generated") + + k:write('pub.pem', 'priv.pem') + local kpub = assert(crypto.pkey.read('pub.pem')) + local kpriv = assert(crypto.pkey.read('priv.pem', true)) + + test_verify(kpub, kpriv) +end + +end + +if not _TEST then lunit.run() end \ No newline at end of file diff --git a/tests/unit/resetiv.lua b/tests/unit/resetiv.lua new file mode 100644 index 0000000..189d5d0 --- /dev/null +++ b/tests/unit/resetiv.lua @@ -0,0 +1,97 @@ +local lunit = require "lunit" +local TEST_CASE = lunit.TEST_CASE + +-- example of AES-256-CTR with custom counter + +local function ichunks(len, chunk_size) + return function(_, b) + b = b + chunk_size + if b > len then return nil end + local e = b + chunk_size - 1 + if e > len then e = len end + return b, e + end, nil, -chunk_size + 1 +end + +-- custom IV counter +-- compatible with WinZip +local function inciv(iv) + for k, v in ipairs(iv) do + if v == 255 then iv[k] = 0 + else iv[k] = v + 1 return iv end + end +end + +-- compatible with OpenSSL +local function std_inciv(iv) + for i = #iv, 1, -1 do + if iv[i] == 255 then iv[i] = 0 + else iv[i] = iv[i] + 1 return iv end + end +end + +local crypto = require "crypto" + +local _ENV = TEST_CASE"resetiv" + +local IV, ctx +local block_size = 16 +local msg = ("012"):rep(3):rep(1333) .. 'abcd' +local KEY = {0x04, 0xF9, 0x4A, 0xFB, 0x60, 0xAF, 0x47, 0x44, 0xD4, 0xDB, 0x9B, 0x3A, 0xE7, 0x23, 0x3E, 0xC6} + +function setup() + IV = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} + ctx = assert_userdata(crypto.encrypt.new('aes-256-ctr', KEY)) +end + +function test_winzip() + local t1 = {} ctx:set_writer(table.insert, t1) + + local t2 = {} + + for b, e in ichunks(#msg, block_size) do + -- use same ctx, do not call msg:sub and use writer + ctx:resetiv(inciv(IV)) + assert_equal(ctx, ctx:update(msg, b, e)) + + -- each time call EVP Init/Update/Final + local chunk = assert_string(crypto.encrypt('aes-256-ctr', msg:sub(b, e), KEY, IV)) + table.insert(t2, chunk) + + assert_equal(t2[#t2], t1[#t1]) + end + + assert_equal(ctx, ctx:final()) + + assert_equal(table.concat(t2), table.concat(t1)) +end + +function test_std() + local etalon = assert_string(crypto.encrypt('aes-256-ctr', msg, KEY, IV)) + + local t = {} ctx:set_writer(table.insert, t) + for b, e in ichunks(#msg, block_size) do + ctx:resetiv(IV) std_inciv(IV) + ctx:update(msg, b, e) + end + + assert_equal(ctx, ctx:final()) + + assert_equal(etalon, table.concat(t)) +end + +function test_fail() + local etalon = assert_string(crypto.encrypt('aes-256-ctr', msg, KEY, IV)) + + local t = {} ctx:set_writer(table.insert, t) + for b, e in ichunks(#msg, block_size) do + ctx:resetiv(IV) inciv(IV) + ctx:update(msg, b, e) + end + + assert_equal(ctx, ctx:final()) + + assert_not_equal(etalon, table.concat(t)) +end + +if not _TEST then lunit.run() end \ No newline at end of file diff --git a/tests/unit/run.lua b/tests/unit/run.lua new file mode 100644 index 0000000..f606446 --- /dev/null +++ b/tests/unit/run.lua @@ -0,0 +1,28 @@ +local HAS_RUNNER = not not lunit + +local lunit = require "lunit" +assert(lunit.TEST_CASE, "lunitx >= 0.7 require") + +local crypto = require "crypto" + +print("------------------------------------") +print("Lua version: " .. (_G.jit and _G.jit.version or _G._VERSION)) +print("LuaCrypto version: " .. crypto._VERSION) +if crypto.openssl_version then + print("OpenSSL version: " .. crypto.openssl_version()) +end +print("------------------------------------") +print("") + +_TEST = true + +require "x509_ca" +require "encrypt" +require "open_seal" +require "update" +require "pkeytest" +require "resetiv" +require "digest" +require "encrypt_large" + +if not HAS_RUNNER then lunit.run() end \ No newline at end of file diff --git a/tests/unit/update.lua b/tests/unit/update.lua new file mode 100644 index 0000000..9b1ee80 --- /dev/null +++ b/tests/unit/update.lua @@ -0,0 +1,115 @@ +local lunit = require "lunit" +local TEST_CASE = lunit.TEST_CASE +local crypto = require 'crypto' + +local _ENV = TEST_CASE "update.range" + +function test_digest() + local str = ("0"):rep(32) + local h1 = crypto.digest.new("md5") + local thex = h1:final(str) + local bhex = h1:final(nil, true) + + assert(thex == crypto.hex(bhex)) + + do + local h2 = crypto.digest.new("md5") + local h = h2:update(str, 1, 16):final(str, 17, 32) + assert(h == thex) + end + + do + local h2 = crypto.digest.new("md5") + local h = h2:update(str, 1, 16):final(str, 17, 32, true) + assert(h == bhex) + end +end + +function test_encrypt() + local str = ( "0"):rep(64) + local key = ( "0"):rep(32) + local iv = ("\0"):rep(16) + local aes1 = crypto.encrypt.new('aes-256-cbc', key, iv) + local edata = aes1:update(str) + edata = edata .. aes1:final() + assert(#edata == 80) + + do + local aes2 = crypto.encrypt.new('aes-256-cbc', key, iv) + local edata2 = aes2:update(str:sub(1, 32)) + edata2 = edata2 .. aes2:update(str:sub(33, 64)) + edata2 = edata2 .. aes2:final() + assert(edata2 == edata) + end + + do + local aes3 = crypto.encrypt.new('aes-256-cbc', key, iv) + local edata3 = aes3:update(str, 1, 32) + edata3 = edata3 .. aes3:update(str, 33, 64) + edata3 = edata3 .. aes3:final() + assert(edata3 == edata) + end + + do + local aes2 = crypto.decrypt.new('aes-256-cbc', key, iv) + local str2 = aes2:update(edata) + str2 = str2 .. aes2:final() + assert(str2 == str) + end + + do + local aes2 = crypto.decrypt.new('aes-256-cbc', key, iv) + local str2 = aes2:update(edata:sub( 1, 32)) + str2 = str2 .. aes2:update(edata:sub(33, 64)) + str2 = str2 .. aes2:update(edata:sub(65, 80)) + str2 = str2 .. aes2:final() + assert(str2 == str) + end + + do + local aes2 = crypto.decrypt.new('aes-256-cbc', key, iv) + local str2 = aes2:update(edata, 1, 32) + str2 = str2 .. aes2:update(edata, 33, 64) + str2 = str2 .. aes2:update(edata, 65, 80) + str2 = str2 .. aes2:final() + assert(str2 == str) + end +end + +function test_hmac() + local str = ("0"):rep(32) + local key = ( "0"):rep(32) + local iv = ("\0"):rep(16) + local h1 = crypto.hmac.new("md5",key) + local thex = h1:final(str) + local bhex = h1:final(nil, true) + + assert(thex == crypto.hex(bhex)) + + do + local h2 = crypto.hmac.new("md5", key) + local h = h2:update(str, 1, 16):final(str, 17, 32) + assert(h == thex) + end + + do + local h2 = crypto.hmac.new("md5", key) + local h = h2:update(str, 1, 16):final(str, 17, 32, true) + assert(h == bhex) + end + +end + +function test_clone() + local key = ("0"):rep(32) + local h1 = crypto.hmac.new("md5", key) + h1:update("123456789") + local h2 = h1:clone() + assert(h2 ~= h1) + assert(tostring(h2) ~= tostring(h1)) + h1:update("qwerty") + h2:update("qwerty") + assert(h1:final() == h2:final()) +end + +if not _TEST then lunit.run() end \ No newline at end of file diff --git a/tests/unit/x509_ca.lua b/tests/unit/x509_ca.lua new file mode 100644 index 0000000..2c0ba40 --- /dev/null +++ b/tests/unit/x509_ca.lua @@ -0,0 +1,143 @@ +local ca_cert = [[-----BEGIN CERTIFICATE----- +MIIG1jCCBL6gAwIBAgIJALZOkAY0D6wQMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD +VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5j +aXNjbzEOMAwGA1UEChMFVmlyZ28xEDAOBgNVBAsTB0hhY2tlcnMxGDAWBgNVBAMT +D0JyYW5kb24gUGhpbGlwczEqMCgGCSqGSIb3DQEJARYbYnJhbmRvbi5waGlsaXBz +QGV4YW1wbGUuY29tMB4XDTEyMDEyNjIyMDAxOFoXDTIyMDEyMzIyMDAxOFowgaIx +CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4g +RnJhbmNpc2NvMQ4wDAYDVQQKEwVWaXJnbzEQMA4GA1UECxMHSGFja2VyczEYMBYG +A1UEAxMPQnJhbmRvbiBQaGlsaXBzMSowKAYJKoZIhvcNAQkBFhticmFuZG9uLnBo +aWxpcHNAZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQC5YVbGpZHLeWDu30o9UUF2yrqizDzbuDshWT8RlVQDdYvvKNFMFt8OCmgabl2k +8T19zMqnWof72iviZDsLGxOmMbMUFvk1TN7cMfWNsD6P/ja4BYxh90Jt8y65yC/T +6Xm1NLqFyhhOPWvzHvhAuvHg5qWvcyzsx3wDoh+dr3hVIJfxq9Ufu8t4JzOjJplQ +8kwklW6CafrcYF85YJqhxObWeL6gYWnYd3AzDE/S4j7C/nNNQah0NZvu6cO/Sc9l +qAw9gI5a6f9Pd7VFAzW7b8jRZ6pMmkNM9rm83i8tjiBgXDIBHZVEhtkC+5Kp0mU6 +ywn/regQGGJ46cInLpCEnDbFhmzOgg4wvNtiy7JT+zyaFQYM0dHPs4JQRfGIc4LO +3M4r5na1qcbNQFQVVbMtsNETg+TrUyrmXDEO9PwwOXU1Khgnrk4A1UWZlf+n5dds +K6KhlRxD+nMzyR+lBPH9vdBtbzoOdy/D/mm6SMKkUIAXWdrPb8ucRMQkxusvq0Dv +8UikFV2Y16r7uqXqiOWCXEKrKMT+cAArCRBzUIoAIWTH4MKoEu9tYRzJcYM+EBc8 +nXrvnUBdE8GNiWW444SPoCTM7SakHnQC34YY6WbEctQhGG2QW1fcPycWO5Aqr4WW +8hrbqjb6X15Y/J5OwqM0n3MrrNNv1nhHqaAVYIQYYlNH2QIDAQABo4IBCzCCAQcw +HQYDVR0OBBYEFLgWlUKomtnlKUEJZHAwCL3pSJt1MIHXBgNVHSMEgc8wgcyAFLgW +lUKomtnlKUEJZHAwCL3pSJt1oYGopIGlMIGiMQswCQYDVQQGEwJVUzETMBEGA1UE +CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEOMAwGA1UEChMF +VmlyZ28xEDAOBgNVBAsTB0hhY2tlcnMxGDAWBgNVBAMTD0JyYW5kb24gUGhpbGlw +czEqMCgGCSqGSIb3DQEJARYbYnJhbmRvbi5waGlsaXBzQGV4YW1wbGUuY29tggkA +tk6QBjQPrBAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAgEAgoSv+w9R +Zlm7XxOD1xKo0AcgtJq6uWic14Y/sVaabN9GPGZVyPI9zywiAPbi9zpBozEWykXz +RA3Tac5Y1xLS+1PCRJcgWiAzIVP0wX0nIj+rxBcXxJ5t7+CiNTQ0BSD3IDgtiWkb +hFYzrUZiTluDs5erR0fatOE4deYLewGErU69rtW+blyCouGzcvBSn/ZmTUhq+VkP +LRfzc1dcHxKw25WL5+O59FkJ3Ytpk7u9xxumNxsugar8ssfs+/Qu5wytVQ1+jPQC +9CFu6n0GNPK3A7ebfUDc7qfUGhvM6YoAvSGK0iRgdDvdqR+47+3UKZ56H7cJufSQ +wNAAWu3CagAXCmQaIKX2C8PS5FxnKymXSZTUVQ55NBcNRrYEfF0+ba6xW9ehSD5N +G2C5FjA7z8eV+FjTDJPBVLZrwutDpyciGT4mpH6ul8rMFTxJuNjzCMT278CEgy5l +IYrjS9B16k/wDfSxtDWyy6laaRnvf7vI+mTTUxZJZVd7ADeW9+Fxx/fddBsFuONw +d81hvMemWe0uz/9SZyb0bmq+ox4zvzS2N05us3vIcZNaIrsG6baVFiTA6js503K5 +KjXgcfC980pnPG37oH41aaAHZEpTGgkpVp92dsHlwkrOjtZO2Tt+c4IUiflkPEvt +QKI92udYACbXW5hT8jwyOo/ugwFMMpNmLvM= +-----END CERTIFICATE-----]] + +local cert_to_verify = [[-----BEGIN CERTIFICATE----- +MIIFxTCCA60CAQEwDQYJKoZIhvcNAQEFBQAwgaIxCzAJBgNVBAYTAlVTMRMwEQYD +VQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMQ4wDAYDVQQK +EwVWaXJnbzEQMA4GA1UECxMHSGFja2VyczEYMBYGA1UEAxMPQnJhbmRvbiBQaGls +aXBzMSowKAYJKoZIhvcNAQkBFhticmFuZG9uLnBoaWxpcHNAZXhhbXBsZS5jb20w +HhcNMTIwMTI2MjIwMjI0WhcNMjIwMTIzMjIwMjI0WjCBrTELMAkGA1UEBhMCVVMx +EzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xGjAY +BgNVBAoTEVZpcmdvIFRlc3QgU2lnbmVyMQ8wDQYDVQQLEwZIYWNrZXIxGDAWBgNV +BAMTD0JyYW5kb24gUGhpbGlwczEqMCgGCSqGSIb3DQEJARYbYnJhbmRvbi5waGls +aXBzQGV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +0Py2Gaq9lsxS7gN4UoF17iV9fI/NW+tgtfgjLqDrNpyvFqlBchhKVeoA2wdaRWpH +uDPWLnwUQNRYu4YVLuAt/32oG9AYzgBEjNMLGdAaqGGSl8HCdnXQh2hJD24WRa1O +dcj+o1kIoUKi5BklBZd+HzTEjbinLUZgCAYxohaIC8yLsZGy6Ez35pAu4XokP9HM +xVM9tZN6HwHI//givYkKv7R6a9iY0fLIHwmEoc4yVw7zNtBqzLLUHROjLCqqvIoi +Zkn7Z4k3080WCD1Q0hQt0SKsf+DCDGS3zaE5EeyVvfBVelqz2v4kFzNf+0lEA411 +UnPEMkfZt+x2Gwr2UAObag961p46Ba+QgifQpyXNQ3bCapqMghfSz6PHeGYeFPNW +QKzVLNQSjnPBc0i0h+AckAFJRzYXWZsh1Jq2TCvTiw+1Irm1m9Ltuv+W85hvhLuB +1AY3runMLQN0eQ0gvjbkcKCKtpoKy4rHtVTiy+8hzL1zaWYu3Bny7BgPiKciiMbo +7TkDzWVX0hIfjcgJAzVogLC1/TVEQkoImomAvzPGXQpbLlX843juVeBCSwdkBAjj +lMoJyGcv6wfO91tkG8PWxUjPQQcJCr8/VSoK10jdUjrQocb3u+ud27n/6eQZOpvw +sbn5q3mr2+zUIon/9k8DbgPEhk6nrCq5rN6A7eCcdwMCAwEAATANBgkqhkiG9w0B +AQUFAAOCAgEAOJfGCRbeByGWHxU1DWTmkqG97NoROUw0Gq9BO3WvxbFCvMettDPz +SF6uUu+C7u5uQ5rCqAB1nDe2uCDljvB6XKBjfk/jbhFBa+56JDKmXxjXRaSLFpX2 +NxByCb48Hir5021Qcebz+ojScwS6O/jpj/sOlGipssICJExBQs0ywlFKbLsM7zRs +v+s0MO5C8cgFO5Yz0KdOXep8rXStaM9N0IZApG+bywBI+1yQbOqP+BUJ95drmXfe +meDJR1/srhxRUicgq1psE2xsd9UEx6AdoakUDv7T2owtVw3PJavNQCW+8ql67DQj +7epQTQ5wVty1ED5PyfHYOlC0LNlUNmoADegUwyYcQ4246ayfqcnJxacQXIpWylF/ +mGHQcR4AmVYsr26UkDYXcwb7BDxH0eb3w5s7X0hwtFzd8jwx3Vagdf4fafm4Vahz +XDiDXVMTZqyncIBu4/8PFfgqgLra/MhHODbLamndPMeHAn0zNXk5HEkiNRhHymSe +oTKkB4Ol10/kEWvOswU/LS69w7HDFgJAnnEi2+XCTHMim8kDcbhoGr2rlL1cT7yL +B3P11S3lepH+PFTFfU19IlrDGfXDxlKNWR9XNVqtQw/qnN+T7XZFW2tuDiMecCYj +64Qm7mwOJZDp1eFU0GiTuF7r7ZMBWTDDe98eOFOOiUZ3m+m43SGVb+U= +-----END CERTIFICATE-----]] + +local bad_cert = [[-----BEGIN CERTIFICATE----- +MIIFxTCCA60CAQEwDQYJKoZIhvcNAQEFBQAwgaIxCzAJBgNVBAYTAlVTMRMwEQYD +VQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMQ4wDAYDVQQK +EwVWaXJnbzEQMA4GA1UECxMHSGFja2VyczEYMBYGA1UEAxMPQnJhbmRvbiBQaGls +aXBzMSowKAYJKoZIhvcNAQkBFhticmFuZG9uLnBoaWxpcHNAZXhhbXBsZS5jb20w +HhcNMTIwMTI2MjIwMjI0WhcNMjIwMTIzMjIwMjI0WjCBrTELMAkGA1UEBhMCVVMx +EzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xGjAY +BgNVBAoTEVZpcmdvIFRlc3QgU2lnbmVyMQ8wDQYDVQQLEwZIYWNrZXIxGDAWBgNV +BAMTD0JyYW5kb24gUGhpbGlwczEqMCgGCSqGSIb3DQEJARYbYnJhbmRvbi5waGls +aXBzQGV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +0Py2Gaq9lsxS7gN4UoF17iV9fI/NW+tgtfgjLqDrNpyvFqlBchhKVeoA2wdaRWpH +uDPWLnwUQNRYu4YVLuAt/32oG9AYzgBEjNMLGdAaqGGSl8HCdnXQh2hJD24WRa1O +dcj+o1kIoUKi5BklBZd+HzTEjbinLUZgCAYxohaIC8yLsZGy6Ez35pAu4XokP9HM +xVM9tZN6HwHI//givYkKv7R6a9iY0fLIHwmEoc4yVw7zNtBqzLLUHROjLCqqvIoi +Zkn7Z4k3080WCD1Q0hQt0SKsf+DCDGS3zaE5EeyVvfBVelqz2v4kFzNf+0lEA411 +UnPEMkfZt+x2Gwr2UAObag961p46Ba+QgifQpyXNQ3bCapqMghfSz6PHeGYeFPNW +QKzVLNQSjnPBc0i0h+AckAFJRzYXWZsh1Jq2TCvTiw+1Irm1m9Ltuv+W85hvhLuB +1AY3runMLQN0eQ0gvjbkcKCKtpoKy4rHtVTiy+8hzL1zaWYu3Bny7BgPiKciiMbo +7TkDzWVX0hIfjcgJAzVogLC1/TVEQkoImomAvzPGXQpbLlX843juVeBCSwdkBAjj +lMoJyGcv6wfO91tkG8PWxUjPQQcJCr8/VSoK10jdUjrQocb3u+ud27n/6eQZOpvw +sbn5q3mr2+zUIon/9k8DbgPEhk6nrCq5rN6A7eCcdwMCAwEAATANBgkqhkiG9w0B +AQUFAAOCAgEAOJfGCRbeByGWHxU1DWTmkqG97NoROUw0Gq9BO3WvxbFCvMettDPz +SF6uUu+C7u5uQ5rCqAB1nDe2uCDljvB6XKBjfk/jbhFBa+56JDKmXxjXRaSLFpX2 +NxByCb48Hir5021Qcebz+ojScwS6O/jpj/sOlGipssICJExBQs0ywlFKbLsM7zRs +v+s0MO5C8cgFO5Yz0KdOXep8rXStaM9N0IZApG+bywBI+1yQbOqP+BUJ95drmXfe +meDJR1/srhxRUicgq1psE2xsd9UEx6AdoakUDv7T2owtVw3PJavNQCW+8ql67DQj +7epQTQ5wVty1ED5PyfHYOlC0LNlUNmoADegUwyYcQ4246ayfqcnJxacQXIpWylF/ +mGHQcR4AmVYsr26UkDYXcwb7BDxH0eb3w5s7X0hwtFzd8jwx3Vagdf4fafm4Vahz +XDiDXVMTZqyncIBu4/8PFfgqgLra/MhHODbLamndPMeHAn0zNXk5HEkiNRhHymSe +oTKkB4Ol10/kEWvOswU/LS69w7HDFgJAnnEi2+XCTHMim8kDcbhoGr3rlL1cT7yL +B3P11S3lepH+PFTFfU19IlrDGfXDxlKNWR9XNVqtQw/qnN+T7XZFW2tuDiMecCYj +64Qm7mwOJZDp1eFU0GiTuF7r7ZMBWTDDe98eOFOOiUZ3m+m43SGVb+U= +-----END CERTIFICATE-----]] + +local lunit = require "lunit" +local TEST_CASE = lunit.TEST_CASE +local crypto = require 'crypto' + +local FAIL = function(str) return function() lunit.fail(str) end end + +local _ENV = TEST_CASE "test x509_ca" +if not crypto.x509_ca then test = FAIL"crypto.x509_ca is unavaliable" else + +local ca + +function setup() + ca = assert_userdata(crypto.x509_ca()) + assert_true(ca:add_pem(ca_cert)) +end + +function teardown() + ca = nil +end + +function test_invalid_cert() + assert_nil(ca:add_pem("FOBAR"), "bad cert succeeded") +end + +function test_verify() + assert_true(ca:verify_pem(cert_to_verify), "failed to verify good cert") +end + +function test_fail() + assert_false(ca:verify_pem(bad_cert), "verified bad cert, noooooo") +end + +end + +if not _TEST then lunit.run() end \ No newline at end of file diff --git a/tests/update.lua b/tests/update.lua new file mode 100644 index 0000000..89a6cf9 --- /dev/null +++ b/tests/update.lua @@ -0,0 +1,119 @@ +local crypto = require "crypto" + +function test_digest() + local str = ("0"):rep(32) + local h1 = crypto.digest.new("md5") + local thex = h1:final(str) + local bhex = h1:final(nil, true) + + assert(thex == crypto.hex(bhex)) + + do + local h2 = crypto.digest.new("md5") + local h = h2:update(str, 1, 16):final(str, 17, 32) + assert(h == thex) + end + + do + local h2 = crypto.digest.new("md5") + local h = h2:update(str, 1, 16):final(str, 17, 32, true) + assert(h == bhex) + end +end + +function test_encrypt() + local str = ( "0"):rep(64) + local key = ( "0"):rep(32) + local iv = ("\0"):rep(16) + local aes1 = crypto.encrypt.new('aes-256-cbc', key, iv) + local edata = aes1:update(str) + edata = edata .. aes1:final() + assert(#edata == 80) + + do + local aes2 = crypto.encrypt.new('aes-256-cbc', key, iv) + local edata2 = aes2:update(str:sub(1, 32)) + edata2 = edata2 .. aes2:update(str:sub(33, 64)) + edata2 = edata2 .. aes2:final() + assert(edata2 == edata) + end + + do + local aes3 = crypto.encrypt.new('aes-256-cbc', key, iv) + local edata3 = aes3:update(str, 1, 32) + edata3 = edata3 .. aes3:update(str, 33, 64) + edata3 = edata3 .. aes3:final() + assert(edata3 == edata) + end + + do + local aes2 = crypto.decrypt.new('aes-256-cbc', key, iv) + local str2 = aes2:update(edata) + str2 = str2 .. aes2:final() + assert(str2 == str) + end + + do + local aes2 = crypto.decrypt.new('aes-256-cbc', key, iv) + local str2 = aes2:update(edata:sub( 1, 32)) + str2 = str2 .. aes2:update(edata:sub(33, 64)) + str2 = str2 .. aes2:update(edata:sub(65, 80)) + str2 = str2 .. aes2:final() + assert(str2 == str) + end + + do + local aes2 = crypto.decrypt.new('aes-256-cbc', key, iv) + local str2 = aes2:update(edata, 1, 32) + str2 = str2 .. aes2:update(edata, 33, 64) + str2 = str2 .. aes2:update(edata, 65, 80) + str2 = str2 .. aes2:final() + assert(str2 == str) + end +end + +function test_hmac() + local str = ("0"):rep(32) + local key = ( "0"):rep(32) + local iv = ("\0"):rep(16) + local h1 = crypto.hmac.new("md5",key) + local thex = h1:final(str) + local bhex = h1:final(nil, true) + + assert(thex == crypto.hex(bhex)) + + do + local h2 = crypto.hmac.new("md5", key) + local h = h2:update(str, 1, 16):final(str, 17, 32) + assert(h == thex) + end + + do + local h2 = crypto.hmac.new("md5", key) + local h = h2:update(str, 1, 16):final(str, 17, 32, true) + assert(h == bhex) + end + +end + +function test_clone() + local key = ("0"):rep(32) + local h1 = crypto.hmac.new("md5", key) + h1:update("123456789") + local h2 = h1:clone() + assert(h2 ~= h1) + assert(tostring(h2) ~= tostring(h1)) + h1:update("qwerty") + h2:update("qwerty") + assert(h1:final() == h2:final()) +end + +test_encrypt() + +test_digest() + +test_hmac() + +test_clone() + +print("Done!")