aboutsummaryrefslogtreecommitdiffstats
path: root/server/resty/hmac.lua
blob: 8d94a8be4bf997b0a7769bd71fa5a5aa7899869f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
local str_util = require "resty.string"
local to_hex = str_util.to_hex
local ffi = require "ffi"
local ffi_new = ffi.new
local ffi_str = ffi.string
local ffi_gc = ffi.gc
local ffi_typeof = ffi.typeof
local C = ffi.C
local setmetatable = setmetatable
local error = error


local _M = { _VERSION = '0.04' }

local mt = { __index = _M }


ffi.cdef[[
typedef struct engine_st ENGINE;
typedef struct evp_pkey_ctx_st EVP_PKEY_CTX;
typedef struct evp_md_ctx_st EVP_MD_CTX;
typedef struct evp_md_st EVP_MD;
typedef struct hmac_ctx_st HMAC_CTX;

//OpenSSL 1.0
void HMAC_CTX_init(HMAC_CTX *ctx);
void HMAC_CTX_cleanup(HMAC_CTX *ctx);

//OpenSSL 1.1
HMAC_CTX *HMAC_CTX_new(void);
void HMAC_CTX_free(HMAC_CTX *ctx);

int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int len, const EVP_MD *md, ENGINE *impl);
int HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, size_t len);
int HMAC_Final(HMAC_CTX *ctx, unsigned char *md, unsigned int *len);

const EVP_MD *EVP_md5(void);
const EVP_MD *EVP_sha1(void);
const EVP_MD *EVP_sha256(void);
const EVP_MD *EVP_sha512(void);
]]

local buf = ffi_new("unsigned char[64]")
local res_len = ffi_new("unsigned int[1]")
local hashes = {
    MD5 = C.EVP_md5(),
    SHA1 = C.EVP_sha1(),
    SHA256 = C.EVP_sha256(),
    SHA512 = C.EVP_sha512()
}

local ctx_new, ctx_free
local openssl11, e = pcall(function ()
    local ctx = C.HMAC_CTX_new()
    C.HMAC_CTX_free(ctx)
end)
if openssl11 then
    ctx_new = function ()
        return C.HMAC_CTX_new()
    end
    ctx_free = function (ctx)
        C.HMAC_CTX_free(ctx)
    end
else
    ffi.cdef [[
    struct evp_md_ctx_st
    {
        const EVP_MD *digest;
        ENGINE *engine;
        unsigned long flags;
        void *md_data;
        EVP_PKEY_CTX *pctx;
        int (*update)(EVP_MD_CTX *ctx,const void *data,size_t count);
    };

    struct evp_md_st
    {
        int type;
        int pkey_type;
        int md_size;
        unsigned long flags;
        int (*init)(EVP_MD_CTX *ctx);
        int (*update)(EVP_MD_CTX *ctx,const void *data,size_t count);
        int (*final)(EVP_MD_CTX *ctx,unsigned char *md);
        int (*copy)(EVP_MD_CTX *to,const EVP_MD_CTX *from);
        int (*cleanup)(EVP_MD_CTX *ctx);

        int (*sign)(int type, const unsigned char *m, unsigned int m_length, unsigned char *sigret, unsigned int *siglen, void *key);
        int (*verify)(int type, const unsigned char *m, unsigned int m_length, const unsigned char *sigbuf, unsigned int siglen, void *key);
        int required_pkey_type[5];
        int block_size;
        int ctx_size;
        int (*md_ctrl)(EVP_MD_CTX *ctx, int cmd, int p1, void *p2);
        };

    struct hmac_ctx_st
    {
        const EVP_MD *md;
        EVP_MD_CTX md_ctx;
        EVP_MD_CTX i_ctx;
        EVP_MD_CTX o_ctx;
        unsigned int key_length;
        unsigned char key[128];
    };
    ]]

    local ctx_ptr_type = ffi_typeof("HMAC_CTX[1]")

    ctx_new = function ()
        local ctx = ffi_new(ctx_ptr_type)
        C.HMAC_CTX_init(ctx)
        return ctx
    end
    ctx_free = function (ctx) 
        C.HMAC_CTX_cleanup(ctx)
    end
end


_M.ALGOS = hashes


function _M.new(self, key, hash_algo)
    local ctx = ctx_new()

    local _hash_algo = hash_algo or hashes.MD5

    if C.HMAC_Init_ex(ctx, key, #key, _hash_algo, nil) == 0 then
        return nil
    end

    ffi_gc(ctx, ctx_free)

    return setmetatable({ _ctx = ctx }, mt)
end


function _M.update(self, s)
    return C.HMAC_Update(self._ctx, s, #s) == 1
end


function _M.final(self, s, hex_output)

    if s ~= nil then
        if C.HMAC_Update(self._ctx, s, #s) == 0 then
            return nil
        end
    end

    if C.HMAC_Final(self._ctx, buf, res_len) == 1 then
        if hex_output == true then
            return to_hex(ffi_str(buf, res_len[0]))
        end
        return ffi_str(buf, res_len[0])
    end

    return nil
end


function _M.reset(self)
    return C.HMAC_Init_ex(self._ctx, nil, 0, nil, nil) == 1
end

return _M