1+ -- Adds HMAC support to Lua with multiple algorithms, via OpenSSL and FFI
2+ --
3+ 4+ -- Date: 16/05/14
5+ --
6+
7+
8+ local ffi = require " ffi"
9+ local ffi_new = ffi .new
10+ local ffi_str = ffi .string
11+ local C = ffi .C
12+ local resty_string = require " resty.string"
13+ local setmetatable = setmetatable
14+ local error = error
15+
16+
17+ local _M = { _VERSION = ' 0.09' }
18+
19+
20+ local mt = { __index = _M }
21+
22+ --
23+ -- EVP_MD is defined in openssl/evp.h
24+ -- HMAC is defined in openssl/hmac.h
25+ --
26+ ffi .cdef [[
27+ typedef struct env_md_st EVP_MD;
28+ typedef struct env_md_ctx_st EVP_MD_CTX;
29+ unsigned char *HMAC (const EVP_MD * evp_md , const void * key , int key_len ,
30+ const unsigned char * d , size_t n , unsigned char * md ,
31+ unsigned int * md_len );
32+ const EVP_MD *EVP_sha1 (void );
33+ const EVP_MD *EVP_sha224 (void );
34+ const EVP_MD *EVP_sha256 (void );
35+ const EVP_MD *EVP_sha384 (void );
36+ const EVP_MD *EVP_sha512 (void );
37+ ]]
38+
39+ -- table definind the available algorithms and the length of each digest
40+ -- for more information @see: http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
41+ local available_algorithms = {
42+ sha1 = { alg = C .EVP_sha1 (), length = 160 / 8 },
43+ sha224 = { alg = C .EVP_sha224 (), length = 224 / 8 },
44+ sha256 = { alg = C .EVP_sha256 (), length = 256 / 8 },
45+ sha384 = { alg = C .EVP_sha384 (), length = 384 / 8 },
46+ sha512 = { alg = C .EVP_sha512 (), length = 512 / 8 }
47+ }
48+
49+ -- 64 is the max lenght and it covers up to sha512 algorithm
50+ local digest_len = ffi_new (" int[?]" , 64 )
51+ local buf = ffi_new (" char[?]" , 64 )
52+
53+
54+ function _M .new (self )
55+ return setmetatable ({}, mt )
56+ end
57+
58+ local function getDigestAlgorithm (dtype )
59+ local md_name = available_algorithms [dtype ]
60+ if ( md_name == nil ) then
61+ error (" attempt to use unknown algorithm: '" .. dtype ..
62+ " '.\n Available algorithms are: sha1,sha224,sha256,sha384,sha512" )
63+ end
64+ return md_name .alg , md_name .length
65+ end
66+
67+ ---
68+ -- Returns the HMAC digest. The hashing algorithm is defined by the dtype parameter.
69+ -- The optional raw flag, defaulted to false, is a boolean indicating whether the output should be a direct binary
70+ -- equivalent of the HMAC or formatted as a hexadecimal string (the default)
71+ --
72+ -- @param self
73+ -- @param dtype The hashing algorithm to use is specified by dtype
74+ -- @param key The secret
75+ -- @param msg The message to be signed
76+ -- @param raw When true, it returns the binary format, else, the hex format is returned
77+ --
78+ function _M .digest (self , dtype , key , msg , raw )
79+ local evp_md , digest_length_int = getDigestAlgorithm (dtype )
80+ if key == nil or msg == nil then
81+ error (" attempt to digest with a null key or message" )
82+ end
83+
84+ C .HMAC (evp_md , key , # key , msg , # msg , buf , digest_len )
85+
86+ if raw == true then
87+ return ffi_str (buf ,digest_length_int )
88+ end
89+
90+ return resty_string .to_hex (ffi_str (buf ,digest_length_int ))
91+ end
92+
93+
94+ return _M
0 commit comments