diff options
Diffstat (limited to 'server/resty/session/ciphers/aes.lua')
-rw-r--r-- | server/resty/session/ciphers/aes.lua | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/server/resty/session/ciphers/aes.lua b/server/resty/session/ciphers/aes.lua new file mode 100644 index 0000000..9a088ad --- /dev/null +++ b/server/resty/session/ciphers/aes.lua @@ -0,0 +1,113 @@ +local aes = require "resty.aes" + +local setmetatable = setmetatable +local tonumber = tonumber +local ceil = math.ceil +local var = ngx.var +local sub = string.sub +local rep = string.rep + +local HASHES = aes.hash + +local CIPHER_MODES = { + ecb = "ecb", + cbc = "cbc", + cfb1 = "cfb1", + cfb8 = "cfb8", + cfb128 = "cfb128", + ofb = "ofb", + ctr = "ctr", + gcm = "gcm", +} + +local CIPHER_SIZES = { + [128] = 128, + [192] = 192, + [256] = 256, +} + +local defaults = { + size = CIPHER_SIZES[tonumber(var.session_aes_size, 10)] or 256, + mode = CIPHER_MODES[var.session_aes_mode] or "cbc", + hash = HASHES[var.session_aes_hash] or HASHES.sha512, + rounds = tonumber(var.session_aes_rounds, 10) or 1, +} + +local function adjust_salt(salt) + if not salt then + return nil + end + + local z = #salt + if z < 8 then + return sub(rep(salt, ceil(8 / z)), 1, 8) + end + if z > 8 then + return sub(salt, 1, 8) + end + + return salt +end + +local function get_cipher(self, key, salt) + local mode = aes.cipher(self.size, self.mode) + if not mode then + return nil, "invalid cipher mode " .. self.mode .. "(" .. self.size .. ")" + end + + return aes:new(key, adjust_salt(salt), mode, self.hash, self.rounds) +end + +local cipher = {} + +cipher.__index = cipher + +function cipher.new(session) + local config = session.aes or defaults + return setmetatable({ + size = CIPHER_SIZES[tonumber(config.size, 10)] or defaults.size, + mode = CIPHER_MODES[config.mode] or defaults.mode, + hash = HASHES[config.hash] or defaults.hash, + rounds = tonumber(config.rounds, 10) or defaults.rounds, + }, cipher) +end + +function cipher:encrypt(data, key, salt, _) + local cip, err = get_cipher(self, key, salt) + if not cip then + return nil, err or "unable to aes encrypt data" + end + + local encrypted_data + encrypted_data, err = cip:encrypt(data) + if not encrypted_data then + return nil, err or "aes encryption failed" + end + + if self.mode == "gcm" then + return encrypted_data[1], nil, encrypted_data[2] + end + + return encrypted_data +end + +function cipher:decrypt(data, key, salt, _, tag) + local cip, err = get_cipher(self, key, salt) + if not cip then + return nil, err or "unable to aes decrypt data" + end + + local decrypted_data + decrypted_data, err = cip:decrypt(data, tag) + if not decrypted_data then + return nil, err or "aes decryption failed" + end + + if self.mode == "gcm" then + return decrypted_data, nil, tag + end + + return decrypted_data +end + +return cipher |