diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 7bbcc01d5945097236ac86b6242522f4aa472ccd..51fd9bcca5d6a73c6e63a30088fd1430d6d3586f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -62,6 +62,7 @@ set_property(DIRECTORY PROPERTY CLEAN_NO_CUSTOM true)
 file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src/lua)
 set(lua_sources)
 lua_source(lua_sources lua/uuid.lua)
+lua_source(lua_sources lua/digest.lua)
 lua_source(lua_sources lua/session.lua)
 
 add_custom_target(generate_lua_sources
diff --git a/src/lua/digest.lua b/src/lua/digest.lua
new file mode 100644
index 0000000000000000000000000000000000000000..3fcf4384476e1410fada7883d08862cd32e98c50
--- /dev/null
+++ b/src/lua/digest.lua
@@ -0,0 +1,111 @@
+-- box.digest.lua (internal file)
+
+do
+
+local ffi = require 'ffi'
+
+ffi.cdef[[
+    /* from openssl/sha.h */
+    unsigned char *SHA(const unsigned char *d, size_t n, unsigned char *md);
+    unsigned char *SHA1(const unsigned char *d, size_t n, unsigned char *md);
+    unsigned char *SHA224(const unsigned char *d, size_t n,unsigned char *md);
+    unsigned char *SHA256(const unsigned char *d, size_t n,unsigned char *md);
+    unsigned char *SHA384(const unsigned char *d, size_t n,unsigned char *md);
+    unsigned char *SHA512(const unsigned char *d, size_t n,unsigned char *md);
+    unsigned char *MD4(const unsigned char *d, size_t n, unsigned char *md);
+
+    /* from openssl/md5.h */
+    unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md);
+
+    /* from libc */
+    int snprintf(char *str, size_t size, const char *format, ...);
+
+
+    typedef uint32_t (*crc32_func)(uint32_t crc,
+        const unsigned char *buf, unsigned int len);
+    extern crc32_func crc32_calc;
+]]
+
+local ssl
+if ssl == nil then
+    pcall(function() ssl = ffi.load('ssl') end)
+end
+
+
+local def = {
+    sha     = { 'SHA',    20 },
+    sha1    = { 'SHA1',   20 },
+    sha224  = { 'SHA224', 28 },
+    sha256  = { 'SHA256', 32 },
+    sha384  = { 'SHA384', 48 },
+    sha512  = { 'SHA512', 64 },
+    md5     = { 'MD5',    16 },
+    md4     = { 'MD4',    16 }
+}
+
+local m = {
+    crc32 = function(str)
+        if str == nil then
+            str = ''
+        else
+            str = tostring(str)
+        end
+        return ffi.C.crc32_calc(4294967295, str, string.len(str))
+    end,
+
+    crc32_update = function(crc, str)
+        if str == nil then
+            str = ''
+        else
+            str = tostring(str)
+        end
+        return ffi.C.crc32_calc(tonumber(crc), str, string.len(str))
+    end
+}
+
+if ssl ~= nil then
+    local hexres = ffi.new('char[129]')
+
+    for pname, df in pairs(def) do
+        local hfunction = df[1]
+        local hsize = df[2]
+
+        m[ pname ] = function(str)
+            if str == nil then
+                str = ''
+            else
+                str = tostring(str)
+            end
+            local r = ssl[hfunction](str, string.len(str), nil)
+            return ffi.string(r, hsize)
+        end
+        
+        m[ pname .. '_hex' ] = function(str)
+            if str == nil then
+                str = ''
+            else
+                str = tostring(str)
+            end
+            local r = ssl[hfunction](str, string.len(str), nil)
+            
+            for i = 0, hsize - 1 do
+                ffi.C.snprintf(hexres + i * 2, 3, "%02x",
+                                ffi.cast('unsigned int', r[i]))
+            end
+            return ffi.string(hexres, hsize * 2)
+        end
+    end
+else
+    local function errorf()
+        error("libSSL was not loaded")
+    end
+    for pname, df in pairs(def) do
+        m[ pname ] = errorf
+        m[ pname .. '_hex' ] = errorf
+    end
+end
+
+
+box.digest = m
+
+end
diff --git a/src/lua/init.cc b/src/lua/init.cc
index d50d016c355d59cc4ea6a3c45da90d5796b5e058..f50877eaf63d02f86889ca5d86044a5c4e896119 100644
--- a/src/lua/init.cc
+++ b/src/lua/init.cc
@@ -76,7 +76,8 @@ struct lua_State *tarantool_L;
 /* contents of src/lua/ files */
 extern char uuid_lua[];
 extern char session_lua[];
-static const char *lua_sources[] = { uuid_lua, session_lua, NULL };
+extern char digest_lua[];
+static const char *lua_sources[] = { uuid_lua, digest_lua, session_lua, NULL };
 
 /**
  * Remember the output of the administrative console in the
diff --git a/test/box/digest.result b/test/box/digest.result
new file mode 100644
index 0000000000000000000000000000000000000000..cf0ae3fe46209ab10217e07b09415070df02c181
--- /dev/null
+++ b/test/box/digest.result
@@ -0,0 +1,148 @@
+lua type(box.digest)
+---
+ - table
+...
+lua box.digest.md4_hex()
+---
+ - 31d6cfe0d16ae931b73c59d7e0c089c0
+...
+lua box.digest.md5_hex()
+---
+ - d41d8cd98f00b204e9800998ecf8427e
+...
+lua box.digest.sha_hex()
+---
+ - f96cea198ad1dd5617ac084a3d92c6107708c0ef
+...
+lua box.digest.sha1_hex()
+---
+ - da39a3ee5e6b4b0d3255bfef95601890afd80709
+...
+lua box.digest.sha224_hex()
+---
+ - d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f
+...
+lua box.digest.sha256_hex()
+---
+ - e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+...
+lua box.digest.sha384_hex()
+---
+ - 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b
+...
+lua box.digest.sha512_hex()
+---
+ - cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e
+...
+lua string.len(box.digest.md4_hex())
+---
+ - 32
+...
+lua string.len(box.digest.md5_hex())
+---
+ - 32
+...
+lua string.len(box.digest.sha_hex())
+---
+ - 40
+...
+lua string.len(box.digest.sha1_hex())
+---
+ - 40
+...
+lua string.len(box.digest.sha224_hex())
+---
+ - 56
+...
+lua string.len(box.digest.sha256_hex())
+---
+ - 64
+...
+lua string.len(box.digest.sha384_hex())
+---
+ - 96
+...
+lua string.len(box.digest.sha512_hex())
+---
+ - 128
+...
+lua string.len(box.digest.md4())
+---
+ - 16
+...
+lua string.len(box.digest.md5())
+---
+ - 16
+...
+lua string.len(box.digest.sha())
+---
+ - 20
+...
+lua string.len(box.digest.sha1())
+---
+ - 20
+...
+lua string.len(box.digest.sha224())
+---
+ - 28
+...
+lua string.len(box.digest.sha256())
+---
+ - 32
+...
+lua string.len(box.digest.sha384())
+---
+ - 48
+...
+lua string.len(box.digest.sha512())
+---
+ - 64
+...
+lua box.digest.md5_hex(123)
+---
+ - 202cb962ac59075b964b07152d234b70
+...
+lua box.digest.md5_hex('123')
+---
+ - 202cb962ac59075b964b07152d234b70
+...
+lua box.digest.md5_hex(true)
+---
+ - b326b5062b2f0e69046810717534cb09
+...
+lua box.digest.md5_hex('true')
+---
+ - b326b5062b2f0e69046810717534cb09
+...
+lua box.digest.md5_hex(nil)
+---
+ - d41d8cd98f00b204e9800998ecf8427e
+...
+lua box.digest.md5_hex()
+---
+ - d41d8cd98f00b204e9800998ecf8427e
+...
+lua box.digest.crc32()
+---
+ - 4294967295
+...
+lua box.digest.crc32_update(4294967295, '')
+---
+ - 4294967295
+...
+lua box.digest.crc32('abc')
+---
+ - 3384066120
+...
+lua box.digest.crc32_update(4294967295, 'abc')
+---
+ - 3384066120
+...
+lua box.digest.crc32('abccde')
+---
+ - 3628146660
+...
+lua box.digest.crc32_update(box.digest.crc32('abc'), 'cde')
+---
+ - 3628146660
+...
diff --git a/test/box/digest.test b/test/box/digest.test
new file mode 100644
index 0000000000000000000000000000000000000000..b121b8d048307c965815cefc21aebb4fada5b293
--- /dev/null
+++ b/test/box/digest.test
@@ -0,0 +1,46 @@
+# encoding: tarantool
+
+exec admin "lua type(box.digest)"
+
+exec admin "lua box.digest.md4_hex()"
+exec admin "lua box.digest.md5_hex()"
+exec admin "lua box.digest.sha_hex()"
+exec admin "lua box.digest.sha1_hex()"
+exec admin "lua box.digest.sha224_hex()"
+exec admin "lua box.digest.sha256_hex()"
+exec admin "lua box.digest.sha384_hex()"
+exec admin "lua box.digest.sha512_hex()"
+
+exec admin "lua string.len(box.digest.md4_hex())"
+exec admin "lua string.len(box.digest.md5_hex())"
+exec admin "lua string.len(box.digest.sha_hex())"
+exec admin "lua string.len(box.digest.sha1_hex())"
+exec admin "lua string.len(box.digest.sha224_hex())"
+exec admin "lua string.len(box.digest.sha256_hex())"
+exec admin "lua string.len(box.digest.sha384_hex())"
+exec admin "lua string.len(box.digest.sha512_hex())"
+
+exec admin "lua string.len(box.digest.md4())"
+exec admin "lua string.len(box.digest.md5())"
+exec admin "lua string.len(box.digest.sha())"
+exec admin "lua string.len(box.digest.sha1())"
+exec admin "lua string.len(box.digest.sha224())"
+exec admin "lua string.len(box.digest.sha256())"
+exec admin "lua string.len(box.digest.sha384())"
+exec admin "lua string.len(box.digest.sha512())"
+
+exec admin "lua box.digest.md5_hex(123)"
+exec admin "lua box.digest.md5_hex('123')"
+exec admin "lua box.digest.md5_hex(true)"
+exec admin "lua box.digest.md5_hex('true')"
+exec admin "lua box.digest.md5_hex(nil)"
+exec admin "lua box.digest.md5_hex()"
+
+exec admin "lua box.digest.crc32()"
+exec admin "lua box.digest.crc32_update(4294967295, '')"
+
+exec admin "lua box.digest.crc32('abc')"
+exec admin "lua box.digest.crc32_update(4294967295, 'abc')"
+
+exec admin "lua box.digest.crc32('abccde')"
+exec admin "lua box.digest.crc32_update(box.digest.crc32('abc'), 'cde')"
diff --git a/test/box/lua.result b/test/box/lua.result
index 738f72ec21f86972fe9bd00298554114d94cc763..20023179eceef3c884d0f0336ea14e77d75f52ca 100644
--- a/test/box/lua.result
+++ b/test/box/lua.result
@@ -19,6 +19,7 @@ lua local t = {} for n in pairs(box) do table.insert(t, '  - box.' .. tostring(n
   - box.cjson
   - box.counter
   - box.delete
+  - box.digest
   - box.dostring
   - box.error
   - box.fiber