From 0c4610c881f206e6fc8afa9a0818746213cef787 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Tue, 10 Aug 2021 12:54:02 +0100 Subject: [PATCH] [Project] Start working on AWS Lua API --- lualib/lua_aws.lua | 126 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 lualib/lua_aws.lua diff --git a/lualib/lua_aws.lua b/lualib/lua_aws.lua new file mode 100644 index 000000000..37c8c3b58 --- /dev/null +++ b/lualib/lua_aws.lua @@ -0,0 +1,126 @@ +--[[ +Copyright (c) 2021, Vsevolod Stakhov + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +]]-- + + +--[[[ +-- @module lua_aws +-- This module contains Amazon AWS utility functions +--]] + +local N = "aws" +local rspamd_logger = require "rspamd_logger" +local lua_util = require "lua_util" +local rspamd_crypto_hash = require "rspamd_cryptobox_hash" + +local exports = {} + +-- Returns a canonical representation of today date +local function today_canonical() + return os.date('!%Y%m%d', os.time()) +end + +--[[[ +-- @function lua_aws.aws_date([date_str]) +-- Returns an aws date header corresponding to the specific date +--]] +local function aws_date(date_str) + if not date_str then + date_str = today_canonical() + end + + return date_str .. 'T000000Z' +end + +exports.aws_date = aws_date + + +-- Local cache of the keys to save resources +local cached_keys = {} + +local function maybe_get_cached_key(date_str, secret_key, region, service, req_type) + local bucket = cached_keys[tonumber(date_str)] + + if not bucket then + return nil + end + + local elt = bucket[string.format('%s.%s.%s.%s', secret_key, region, service, req_type)] + if elt then + return elt + end +end + +local function save_cached_key(date_str, secret_key, region, service, req_type, key) + local numdate = tonumber(date_str) + -- expire old buckets + for k,_ in pairs(cached_keys) do + if k < numdate then + cached_keys[k] = nil + end + end + + + local bucket = cached_keys[tonumber(date_str)] + local idx = string.format('%s.%s.%s.%s', secret_key, region, service, req_type) + + if not bucket then + cached_keys[tonumber(date_str)] = { + idx = key + } + else + bucket[idx] = key + end +end +--[[[ +-- @function lua_aws.aws_signing_key([date_str], secret_key, region, [service='s3'], [req_type='aws4_request']) +-- Returns a signing key for the specific parameters +--]] +local function aws_signing_key(date_str, secret_key, region, service, req_type) + if not date_str then + date_str = today_canonical() + end + + if not service then + service = 's3' + end + + if not req_type then + req_type = 'aws4_request' + end + + assert(type(secret_key) == 'string') + assert(type(region) == 'string') + + local maybe_cached = maybe_get_cached_key(date_str, secret_key, region, service, req_type) + + if maybe_cached then + return maybe_cached + end + + local hmac1 = rspamd_crypto_hash.create_specific_keyed("AWS4" .. secret_key, "sha256", date_str):bin() + local hmac2 = rspamd_crypto_hash.create_specific_keyed(hmac1, "sha256", date_str):bin() + local hmac3 = rspamd_crypto_hash.create_specific_keyed(hmac2, "sha256",region):bin() + local hmac4 = rspamd_crypto_hash.create_specific_keyed(hmac3, "sha256", service):bin() + local final_key = rspamd_crypto_hash.create_specific_keyed(hmac4, "sha256", req_type):bin() + + save_cached_key(date_str, secret_key, region, service, req_type, final_key) + + return final_key +end + +exports.aws_signing_key = aws_signing_key + +return exports -- 2.39.5