├── .gitignore ├── Makefile ├── lua-resty-scrypt-dev-1.rockspec ├── LICENSE ├── lib └── resty │ └── scrypt.lua └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | **/*.o 4 | **/*.so -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OPENRESTY_PREFIX=/usr/local/openresty 2 | 3 | PREFIX ?= /usr/local 4 | LUA_INCLUDE_DIR ?= $(PREFIX)/include 5 | LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION) 6 | INSTALL ?= install 7 | 8 | .PHONY: all install 9 | 10 | all: ; 11 | 12 | install: all 13 | $(INSTALL) -d $(DESTDIR)/$(LUA_LIB_DIR)/resty 14 | $(INSTALL) lib/resty/*.lua $(DESTDIR)/$(LUA_LIB_DIR)/resty 15 | -------------------------------------------------------------------------------- /lua-resty-scrypt-dev-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-resty-scrypt" 2 | version = "dev-1" 3 | source = { 4 | url = "git://github.com/bungle/lua-resty-scrypt.git" 5 | } 6 | description = { 7 | summary = "LuaJIT FFI-based scrypt library for OpenResty.", 8 | detailed = "lua-resty-scrypt is a scrypt (password) hashing library for OpenResty.", 9 | homepage = "https://github.com/bungle/lua-resty-scrypt", 10 | maintainer = "Aapo Talvensaari ", 11 | license = "BSD" 12 | } 13 | dependencies = { 14 | "lua >= 5.1" 15 | } 16 | build = { 17 | type = "builtin", 18 | modules = { 19 | ["resty.scrypt"] = "lib/resty/scrypt.lua" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Aapo Talvensaari 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /lib/resty/scrypt.lua: -------------------------------------------------------------------------------- 1 | local ffi = require "ffi" 2 | local ffi_cdef = ffi.cdef 3 | local ffi_new = ffi.new 4 | local ffi_str = ffi.string 5 | local ffi_load = ffi.load 6 | local ffi_typeof = ffi.typeof 7 | local type = type 8 | local tonumber = tonumber 9 | local tostring = tostring 10 | local format = string.format 11 | local function unavailable() return nil end 12 | local ok, rnd = pcall(require, "resty.random") 13 | local bytes = ok and rnd.bytes or unavailable 14 | local ok, str = pcall(require, "resty.string") 15 | local tohex = ok and str.to_hex or unavailable 16 | ffi_cdef[[ 17 | int crypto_scrypt( 18 | const uint8_t *passwd, 19 | size_t passwdlen, 20 | const uint8_t *salt, 21 | size_t saltlen, 22 | uint64_t N, 23 | uint32_t _r, 24 | uint32_t _p, 25 | uint8_t *buf, 26 | size_t buflen); 27 | int calibrate( 28 | size_t maxmem, 29 | double maxmemfrac, 30 | double maxtime, 31 | uint64_t *n, 32 | uint32_t *r, 33 | uint32_t *p); 34 | ]] 35 | local scrypt = ffi_load "scrypt" 36 | local s = 32 37 | local t = ffi_typeof "uint8_t[?]" 38 | local n = ffi_new("uint64_t[1]", 32768) 39 | local r = ffi_new("uint32_t[1]", 8) 40 | local p = ffi_new("uint32_t[1]", 1) 41 | local b = ffi_new(t, s) 42 | local function random(len) 43 | local b = bytes(len, true) or bytes(len) 44 | return b and tohex(b) or nil 45 | end 46 | local function crypt(opts) 47 | local secret, salt, saltsize, keysize = '', nil, 8, 32 48 | if type(opts) ~= "table" then 49 | secret = tostring(opts) 50 | else 51 | if type(opts.secret) == "string" then secret = opts.secret end 52 | if type(opts.keysize) == "number" then 53 | if opts.keysize < 16 then keysize = 16 54 | elseif opts.keysize > 512 then keysize = 512 55 | else keysize = opts.keysize end 56 | if keysize ~= s then 57 | s = keysize 58 | b = ffi_new(t, s) 59 | end 60 | end 61 | if type(opts.n) == "number" then 62 | if n[0] ~= opts.n then n[0] = opts.n end 63 | end 64 | if type(opts.r) == "number" then 65 | if r[0] ~= opts.r then r[0] = opts.r end 66 | end 67 | if type(opts.p) == "number" then 68 | if p[0] ~= opts.p then p[0] = opts.p end 69 | end 70 | if type(opts.salt) == "string" then 71 | salt = opts.salt 72 | end 73 | if type(opts.saltsize) == "number" then 74 | if opts.saltsize < 8 then 75 | saltsize = 8 76 | elseif (opts.saltsize > 32) then 77 | saltsize = 32 78 | else 79 | saltsize = opts.saltsize 80 | end 81 | end 82 | end 83 | if not salt then 84 | salt = random(saltsize) 85 | if not salt then 86 | return nil, "Unable to generate random salt" 87 | end 88 | end 89 | if scrypt.crypto_scrypt(secret, #secret, salt, #salt, n[0], r[0], p[0], b, s) == 0 then 90 | return format("%02x$%02x$%02x$%s$%s", tonumber(n[0]), r[0], p[0], salt, tohex(ffi_str(b, s))) 91 | end 92 | return nil, "Unable to call scrypt" 93 | end 94 | local function check(secret, hash) 95 | local opts = {} 96 | local n, r, p, salt = hash:match(("([^$]*)$"):rep(5)) 97 | opts.secret = secret 98 | opts.salt = salt 99 | opts.n = tonumber(n, 16) 100 | opts.r = tonumber(r, 16) 101 | opts.p = tonumber(p, 16) 102 | return crypt(opts) == hash 103 | end 104 | local function calibrate(maxmem, maxmemfrac, maxtime) 105 | if type(maxmem) ~= "number" then maxmem = 1048576 end 106 | if type(maxmemfrac) ~= "number" then maxmemfrac = 0.5 end 107 | if type(maxtime) ~= "number" then maxtime = 0.2 end 108 | if (scrypt.calibrate(maxmem, maxmemfrac, maxtime, n, r, p) == 0) then 109 | return tonumber(n[0]), r[0], p[0] 110 | else 111 | return nil, "Unable to call scrypt calibrate" 112 | end 113 | end 114 | local function memoryuse(n, r, p) 115 | return 128 * (r or 8) * (p or 1) + 256 * (r or 8) + 128 * (r or 8) * (n or 32768); 116 | end 117 | return { 118 | crypt = crypt, 119 | check = check, 120 | calibrate = calibrate, 121 | memoryuse = memoryuse 122 | } 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lua-resty-scrypt 2 | 3 | `lua-resty-scrypt` is a scrypt (password) hashing library for OpenResty. 4 | 5 | ## Hello World with lua-resty-scrypt 6 | 7 | ```lua 8 | local scrypt = require "resty.scrypt" 9 | local hash = scrypt.crypt "My Secret" -- returns a hash that can be stored in db 10 | local valid = scrypt.check("My Secret", hash) -- valid holds true 11 | local valid = scrypt.check("My Guess", hash) -- valid holds false 12 | 13 | local n,r,p = scrypt.calibrate() -- returns n,r,p calibration values 14 | ``` 15 | 16 | ## Installation 17 | 18 | Just place [`scrypt.lua`](https://github.com/bungle/lua-resty-scrypt/blob/master/lib/resty/scrypt.lua) somewhere in your `package.path`, preferably under `resty` directory. If you are using OpenResty, the default location would be `/usr/local/openresty/lualib/resty`. 19 | 20 | ### Compiling and Installing Scrypt C-library 21 | 22 | These are just rudimentary notes. Better installation instructions will follow: 23 | 24 | 1. First download Scrypt from here: https://github.com/bungle/lua-scrypt 25 | 2. Run `make` 26 | 4. Place `scrypt.so|scrypt.dylib|scrypt.dll` somewhere in the default search path for dynamic libraries of your operating system (or modify `scrypt.lua` and point `ffi_load("scrypt")` with full path to `scrypt.so|scrypt.dylib|scrypt.dll`, e.g. `local scrypt = ffi_load("/usr/local/lib/lua/5.1/scrypt.so")`). 27 | 28 | ### Using LuaRocks or MoonRocks 29 | 30 | If you are using LuaRocks >= 2.2: 31 | 32 | ```Shell 33 | $ luarocks install lua-resty-scrypt 34 | ``` 35 | 36 | If you are using LuaRocks < 2.2: 37 | 38 | ```Shell 39 | $ luarocks install --server=http://rocks.moonscript.org moonrocks 40 | $ moonrocks install lua-resty-scrypt 41 | ``` 42 | 43 | MoonRocks repository for `lua-resty-scrypt` is located here: https://rocks.moonscript.org/modules/bungle/lua-resty-scrypt. 44 | 45 | ## Lua API 46 | 47 | #### string scrypt.crypt(opts) 48 | 49 | Uses scrypt algorithm to generate hash from the input. Input parameter `opts` can 50 | either be `string` (a `secret`) or a table. If it is a table you may pass in some 51 | configuration parameters as well. Available table options (defaults are as follows): 52 | 53 | ```lua 54 | local opts = { 55 | secret = "", 56 | keysize = 32, 57 | n = 32768, 58 | r = 8, 59 | p = 1, 60 | salt = "random (saltsize) bytes generated with OpenSSL", 61 | saltsize = 8 62 | } 63 | ``` 64 | 65 | If you pass opts anything other than a table, it will be `tostring`ified and used 66 | as a `secret`. `keysize` can be between 16 and 512, `saltsize` can be between 8 67 | and 32. 68 | 69 | This function returns string that looks like this: 70 | 71 | ```lua 72 | n$r$p$salt$hash 73 | ``` 74 | 75 | All parts present a `hex dump` of their values. 76 | 77 | ##### Example 78 | 79 | ```lua 80 | local h1 = scrypt.crypt "My Secret" 81 | local h2 = scrypt.crypt{ 82 | secret = "My Secret", 83 | keysize = 512 84 | } 85 | ``` 86 | 87 | #### boolean scrypt.check(secret, hash) 88 | 89 | With this function you can check if the `secret` really matches with the `hash` that 90 | was generated with `scrypt.crypt` from the same `secret`. The `hash` contains also the 91 | configuration parameters like `n`, `r`, `p` and `salt`. 92 | 93 | ##### Example 94 | 95 | ```lua 96 | local b1 = scrypt.check("My Secret", scrypt.crypt "My Secret") -- returns true 97 | local b2 = scrypt.check("My Secret", scrypt.crypt "No Secret") -- returns false 98 | ``` 99 | 100 | #### number, number, number scrypt.calibrate(maxmem, maxmemfrac, maxtime) 101 | 102 | This function can be used to count `n`, `r`, and `p` configuration values from 103 | `maxmem`, `maxmemfrac` and `maxtime` parameters. These are the defaults for those: 104 | 105 | ```lua 106 | maxmem = 1048576 107 | maxmemfrac = 0.5 108 | maxtime = 0.2 109 | ``` 110 | 111 | The results may change depending on your computer's processing power. 112 | 113 | ##### Example 114 | 115 | ```lua 116 | local n,r,p = scrypt.calibrate() 117 | local hash = scrypt.crypt{ 118 | secret = "My Secret", 119 | n = n, 120 | r = r, 121 | p = p 122 | } 123 | ``` 124 | 125 | #### number scrypt.memoryuse(n, r, p) 126 | 127 | Counts the memory use of scrypt-algorigth with the provided `n`, `r`, and `p` 128 | arguments. 129 | 130 | ##### Example 131 | 132 | ```lua 133 | local memoryuse = scrypt.memoryuse(scrypt.calibrate()) 134 | ``` 135 | 136 | Default parameters for `n`, `r`, and `p` are: 137 | 138 | ```lua 139 | n = 32768 140 | r = 8 141 | p = 1 142 | ``` 143 | 144 | ## License 145 | 146 | `lua-resty-scrypt` uses two clause BSD license. 147 | 148 | ``` 149 | Copyright (c) 2014, Aapo Talvensaari 150 | All rights reserved. 151 | 152 | Redistribution and use in source and binary forms, with or without modification, 153 | are permitted provided that the following conditions are met: 154 | 155 | * Redistributions of source code must retain the above copyright notice, this 156 | list of conditions and the following disclaimer. 157 | 158 | * Redistributions in binary form must reproduce the above copyright notice, this 159 | list of conditions and the following disclaimer in the documentation and/or 160 | other materials provided with the distribution. 161 | 162 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 163 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 164 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 165 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 166 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 167 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 168 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 169 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 170 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 171 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 172 | ``` 173 | --------------------------------------------------------------------------------