diff --git a/src/lua/crypto.lua b/src/lua/crypto.lua index d63f5a0151fd6594a1355e24cebbb332b28d5a40..bd9019c8a186f35fd07b587b8b19312067c4a163 100644 --- a/src/lua/crypto.lua +++ b/src/lua/crypto.lua @@ -4,6 +4,12 @@ local ffi = require 'ffi' local buffer = require('buffer') ffi.cdef[[ + /* from openssl/err.h */ + unsigned long ERR_get_error(void); + char *ERR_error_string(unsigned long e, char *buf); + void ERR_load_ERR_strings(void); + void ERR_load_crypto_strings(void); + /* from openssl/evp.h */ void OpenSSL_add_all_digests(); void OpenSSL_add_all_ciphers(); @@ -50,11 +56,16 @@ if ssl == nil then if ssl ~= nil then ssl.OpenSSL_add_all_digests() ssl.OpenSSL_add_all_ciphers() + ssl.ERR_load_crypto_strings() break end end end +local function openssl_err_str() + return ffi.string(ssl.ERR_error_string(ssl.ERR_get_error(), nil)) +end + local digests = {} if ssl then for class, name in pairs({ @@ -62,7 +73,10 @@ if ssl then sha = 'SHA', sha1 = 'SHA1', sha224 = 'SHA224', sha256 = 'SHA256', sha384 = 'SHA384', sha512 = 'SHA512', dss = 'DSS', dss1 = 'DSS1', mdc2 = 'MDC2', ripemd160 = 'RIPEMD160'}) do - digests[class] = ssl.EVP_get_digestbyname(class) + local digest = ssl.EVP_get_digestbyname(class) + if digest ~= nil then + digests[class] = digest + end end end @@ -75,7 +89,7 @@ end local function digest_new(digest) local ctx = ssl.EVP_MD_CTX_create() if ctx == nil then - return error('Can\'t create digest ctx') + return error('Can\'t create digest ctx: ' .. openssl_err_str()) end ffi.gc(ctx, digest_gc) local self = setmetatable({ @@ -94,7 +108,7 @@ local function digest_init(self) return error('Digest context isn\'t usable') end if ssl.EVP_DigestInit_ex(self.ctx, self.digest, nil) ~= 1 then - return error('Can\'t init digest') + return error('Can\'t init digest: ' .. openssl_err_str()) end self.initialized = true end @@ -104,7 +118,7 @@ local function digest_update(self, input) return error('Digest not initialized') end if ssl.EVP_DigestUpdate(self.ctx, input, input:len()) ~= 1 then - return error('Can\'t update digest') + return error('Can\'t update digest: ' .. openssl_err_str()) end end @@ -114,7 +128,7 @@ local function digest_final(self) end self.initialized = false if ssl.EVP_DigestFinal_ex(self.ctx, self.buf.wpos, self.outl) ~= 1 then - return error('Can\'t finalize digest') + return error('Can\'t finalize digest: ' .. openssl_err_str()) end return ffi.string(self.buf.wpos, self.outl[0]) end @@ -142,8 +156,11 @@ if ssl then local algo_api = {} for mode, mode_name in pairs({cfb = 'CFB', ofb = 'OFB', cbc = 'CBC', ecb = 'ECB'}) do - algo_api[mode] = + local cipher = ssl.EVP_get_cipherbyname(algo_name .. '-' .. mode_name) + if cipher ~= nil then + algo_api[mode] = cipher + end end if algo_api ~= {} then ciphers[algo] = algo_api @@ -158,15 +175,24 @@ local function cipher_gc(ctx) end local function cipher_new(cipher, key, iv, direction) + local block_size = ssl.EVP_CIPHER_block_size(cipher) + if key == nil or key:len() < block_size then + return error('Key length should be equal cipher block size (' + .. tostring(block_size) .. ')') + end + if iv == nil or iv:len() < block_size then + return error('Initial vector length should be equal cipher block size (' + .. tostring(block_size) .. ')') + end local ctx = ssl.EVP_CIPHER_CTX_new() if ctx == nil then - return error('Can\'t create cipher ctx') + return error('Can\'t create cipher ctx: ' .. openssl_err_str()) end ffi.gc(ctx, cipher_gc) local self = setmetatable({ ctx = ctx, cipher = cipher, - block_size = ssl.EVP_CIPHER_block_size(cipher), + block_size = block_size, direction = direction, buf = buffer.ibuf(), initialized = false, @@ -180,9 +206,9 @@ local function cipher_init(self, key, iv) if self.ctx == nil then return error('Cipher context isn\'t usable') end - if ssl.EVP_CipherInit_ex(self.ctx, self.cipher, nil, + if ssl.EVP_CipherInit_ex(self.ctx, self.cipher, nil, key, iv, self.direction) ~= 1 then - return error('Can\'t init cipher') + return error('Can\'t init cipher:' .. openssl_err_str()) end self.initialized = true end @@ -191,9 +217,13 @@ local function cipher_update(self, input) if not self.initialized then return error('Cipher not initialized') end + if input == nil then + return '' + end + input = tostring(input) local wpos = self.buf:reserve(input:len() + self.block_size - 1) if ssl.EVP_CipherUpdate(self.ctx, wpos, self.outl, input, input:len()) ~= 1 then - return error('Can\'t update cipher') + return error('Can\'t update cipher:' .. openssl_err_str()) end return ffi.string(wpos, self.outl[0]) end @@ -205,7 +235,7 @@ local function cipher_final(self) self.initialized = false local wpos = self.buf:reserve(self.block_size - 1) if ssl.EVP_CipherFinal_ex(self.ctx, wpos, self.outl) ~= 1 then - return error('Can\'t finalize cipher') + return error('Can\'t finalize cipher:' .. openssl_err_str()) end self.initialized = false return ffi.string(wpos, self.outl[0]) @@ -254,7 +284,7 @@ digest_api = setmetatable(digest_api, ' is not supported or SSL library not found') end }) local function cipher_mode_error(self, mode) - error('Cipher mode' .. mode .. ' is not supported') + error('Cipher mode ' .. mode .. ' is not supported') end local cipher_api = {} diff --git a/test/app/crypto.result b/test/app/crypto.result new file mode 100644 index 0000000000000000000000000000000000000000..085c0803548ef5b2d310ec336ffdd07fbf982bc9 --- /dev/null +++ b/test/app/crypto.result @@ -0,0 +1,110 @@ +crypto = require('crypto') +--- +... +type(crypto) +--- +- table +... +ciph = crypto.cipher.aes128.cbc +--- +... +pass = '12345678876543211234567887654321' +--- +... +iv = 'abcdefghijklmnopqrstuvwxyz123456' +--- +... +enc = ciph.encrypt('test', pass, iv) +--- +... +enc +--- +- !!binary WpJJu6l6oziZcyvND8KueA== +... +ciph.decrypt(enc, pass, iv) +--- +- test +... +--Failing scenaries +crypto.cipher.aes128.cbc.encrypt('a') +--- +- error: 'builtin/crypto.lua:302: Key length should be equal cipher block size (16)' +... +crypto.cipher.aes128.cbc.encrypt('a', '123456', '435') +--- +- error: 'builtin/crypto.lua:302: Key length should be equal cipher block size (16)' +... +crypto.cipher.aes128.cbc.encrypt('a', '1234567887654321') +--- +- error: 'builtin/crypto.lua:302: Initial vector length should be equal cipher block + size (16)' +... +crypto.cipher.aes128.cbc.encrypt('a', '1234567887654321', '12') +--- +- error: 'builtin/crypto.lua:302: Initial vector length should be equal cipher block + size (16)' +... +crypto.cipher.aes256.cbc.decrypt('a') +--- +- error: 'builtin/crypto.lua:302: Key length should be equal cipher block size (16)' +... +crypto.cipher.aes256.cbc.decrypt('a', '123456', '435') +--- +- error: 'builtin/crypto.lua:302: Key length should be equal cipher block size (16)' +... +crypto.cipher.aes256.cbc.decrypt('a', '12345678876543211234567887654321') +--- +- error: 'builtin/crypto.lua:302: Initial vector length should be equal cipher block + size (16)' +... +crypto.cipher.aes256.cbc.decrypt('12', '12345678876543211234567887654321', '12') +--- +- error: 'builtin/crypto.lua:302: Initial vector length should be equal cipher block + size (16)' +... +crypto.cipher.aes192.cbc.encrypt.new() +--- +- error: Key length should be equal cipher block size (16) +... +crypto.cipher.aes192.cbc.encrypt.new('123321') +--- +- error: Key length should be equal cipher block size (16) +... +crypto.cipher.aes192.cbc.decrypt.new('123456788765432112345678') +--- +- error: Initial vector length should be equal cipher block size (16) +... +crypto.cipher.aes192.cbc.decrypt.new('123456788765432112345678', '12345') +--- +- error: Initial vector length should be equal cipher block size (16) +... +crypto.cipher.aes100.efb +--- +- error: 'builtin/crypto.lua:318: Cipher method aes100 is not supported or SSL library + not found' +... +crypto.cipher.aes256.nomode +--- +- error: 'builtin/crypto.lua:287: Cipher mode nomode is not supported' +... +crypto.digest.nodigest +--- +- error: 'builtin/crypto.lua:283: Digest method nodigest is not supported or SSL library + not found' +... +bad_pass = '87654321123456788765432112345678' +--- +... +bad_iv = '123456abcdefghijklmnopqrstuvwxyz' +--- +... +ciph.decrypt(enc, bad_pass, iv) +--- +- error: 'builtin/crypto.lua:304: Can''t finalize cipher:error:06065064:digital envelope + routines:EVP_DecryptFinal_ex:bad decrypt' +... +ciph.decrypt(enc, pass, bad_iv) +--- +- error: 'builtin/crypto.lua:304: Can''t finalize cipher:error:06065064:digital envelope + routines:EVP_DecryptFinal_ex:bad decrypt' +... diff --git a/test/app/crypto.test.lua b/test/app/crypto.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..b42c63be957380ac3bda0899b9ca7871933fcbbe --- /dev/null +++ b/test/app/crypto.test.lua @@ -0,0 +1,38 @@ +crypto = require('crypto') +type(crypto) + +ciph = crypto.cipher.aes128.cbc +pass = '12345678876543211234567887654321' +iv = 'abcdefghijklmnopqrstuvwxyz123456' +enc = ciph.encrypt('test', pass, iv) +enc +ciph.decrypt(enc, pass, iv) + + +--Failing scenaries +crypto.cipher.aes128.cbc.encrypt('a') +crypto.cipher.aes128.cbc.encrypt('a', '123456', '435') +crypto.cipher.aes128.cbc.encrypt('a', '1234567887654321') +crypto.cipher.aes128.cbc.encrypt('a', '1234567887654321', '12') + +crypto.cipher.aes256.cbc.decrypt('a') +crypto.cipher.aes256.cbc.decrypt('a', '123456', '435') +crypto.cipher.aes256.cbc.decrypt('a', '12345678876543211234567887654321') +crypto.cipher.aes256.cbc.decrypt('12', '12345678876543211234567887654321', '12') + +crypto.cipher.aes192.cbc.encrypt.new() +crypto.cipher.aes192.cbc.encrypt.new('123321') +crypto.cipher.aes192.cbc.decrypt.new('123456788765432112345678') +crypto.cipher.aes192.cbc.decrypt.new('123456788765432112345678', '12345') + +crypto.cipher.aes100.efb +crypto.cipher.aes256.nomode + +crypto.digest.nodigest + + +bad_pass = '87654321123456788765432112345678' +bad_iv = '123456abcdefghijklmnopqrstuvwxyz' +ciph.decrypt(enc, bad_pass, iv) +ciph.decrypt(enc, pass, bad_iv) + diff --git a/test/app/digest.result b/test/app/digest.result index 0ed6bcdbd18633bc9561c9a4bcddeaf54cdd5b69..ad6f88666368a55179cabfa2249399ddf688e321 100644 --- a/test/app/digest.result +++ b/test/app/digest.result @@ -326,7 +326,8 @@ digest.aes256cbc.decrypt(digest.aes256cbc.encrypt('test123', 'passpasspasspasspa ... digest.aes256cbc.decrypt(digest.aes256cbc.encrypt('test123', 'passpasspasspasspasspasspasspass', 'iv12tras8712cvbhuytrghjklmnbdr34'), 'nosspasspasspasspasspasspasspass', 'iv12tras8712cvbhuytrghjklmnbdr34') --- -- error: 'builtin/crypto.lua:274: Can''t finalize cipher' +- error: 'builtin/crypto.lua:304: Can''t finalize cipher:error:06065064:digital envelope + routines:EVP_DecryptFinal_ex:bad decrypt' ... digest = nil ---