core/plugin/flusher/sls/SLSUtil.cpp (224 lines of code) (raw):
// Copyright 2024 iLogtail Authors
//
// 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.
#include "plugin/flusher/sls/SLSUtil.h"
#include "app_config/AppConfig.h"
#include "common/EncodingUtil.h"
#include "common/HashUtil.h"
#include "common/TimeUtil.h"
#include "common/http/Constant.h"
#include "plugin/flusher/sls/SLSConstant.h"
using namespace std;
namespace logtail {
static string DATE_FORMAT_RFC822 = "%a, %d %b %Y %H:%M:%S GMT";
#define BIT_COUNT_WORDS 2
#define BIT_COUNT_BYTES (BIT_COUNT_WORDS * sizeof(uint32_t))
/*
* define the rotate left (circular left shift) operation
*/
#define rotl(v, b) (((v) << (b)) | ((v) >> (32 - (b))))
/*
* Define the basic SHA-1 functions F1 ~ F4. Note that the exclusive-OR
* operation (^) in F1 and F3 may be replaced by a bitwise OR operation
* (|), which produce identical results.
*
* F1 is used in ROUND 0~19, F2 is used in ROUND 20~39
* F3 is used in ROUND 40~59, F4 is used in ROUND 60~79
*/
#define F1(B, C, D) (((B) & (C)) ^ (~(B) & (D)))
#define F2(B, C, D) ((B) ^ (C) ^ (D))
#define F3(B, C, D) (((B) & (C)) ^ ((B) & (D)) ^ ((C) & (D)))
#define F4(B, C, D) ((B) ^ (C) ^ (D))
/*
* Use different K in different ROUND
*/
#define K00_19 0x5A827999
#define K20_39 0x6ED9EBA1
#define K40_59 0x8F1BBCDC
#define K60_79 0xCA62C1D6
/*
* Another implementation of the ROUND transformation:
* (here the T is a temp variable)
* For t=0 to 79:
* {
* T=rotl(A,5)+Func(B,C,D)+K+W[t]+E;
* E=D; D=C; C=rotl(B,30); B=A; A=T;
* }
*/
#define ROUND(t, A, B, C, D, E, Func, K) \
E += rotl(A, 5) + Func(B, C, D) + W[t] + K; \
B = rotl(B, 30);
#define ROUND5(t, Func, K) \
ROUND(t, A, B, C, D, E, Func, K); \
ROUND(t + 1, E, A, B, C, D, Func, K); \
ROUND(t + 2, D, E, A, B, C, Func, K); \
ROUND(t + 3, C, D, E, A, B, Func, K); \
ROUND(t + 4, B, C, D, E, A, Func, K)
#define ROUND20(t, Func, K) \
ROUND5(t, Func, K); \
ROUND5(t + 5, Func, K); \
ROUND5(t + 10, Func, K); \
ROUND5(t + 15, Func, K)
/*
* Define constant of the initial vector
*/
const uint32_t SHA1::IV[SHA1_DIGEST_WORDS] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0};
/*
* the message must be the big-endian32 (or left-most word)
* before calling the transform() function.
*/
const static uint32_t iii = 1;
const static bool littleEndian = *(uint8_t*)&iii != 0;
inline uint32_t littleEndianToBig(uint32_t d) {
uint8_t* data = (uint8_t*)&d;
return data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
}
inline void make_big_endian32(uint32_t* data, unsigned n) {
if (!littleEndian) {
return;
}
for (; n > 0; ++data, --n) {
*data = littleEndianToBig(*data);
}
}
inline size_t min(size_t a, size_t b) {
return a < b ? a : b;
}
void SHA1::transform() {
uint32_t W[80];
memcpy(W, M, SHA1_INPUT_BYTES);
memset((uint8_t*)W + SHA1_INPUT_BYTES, 0, sizeof(W) - SHA1_INPUT_BYTES);
for (unsigned t = 16; t < 80; t++) {
W[t] = rotl(W[t - 16] ^ W[t - 14] ^ W[t - 8] ^ W[t - 3], 1);
}
uint32_t A = H[0];
uint32_t B = H[1];
uint32_t C = H[2];
uint32_t D = H[3];
uint32_t E = H[4];
ROUND20(0, F1, K00_19);
ROUND20(20, F2, K20_39);
ROUND20(40, F3, K40_59);
ROUND20(60, F4, K60_79);
H[0] += A;
H[1] += B;
H[2] += C;
H[3] += D;
H[4] += E;
}
void SHA1::add(const uint8_t* data, size_t data_len) {
unsigned mlen = (unsigned)((bits >> 3) % SHA1_INPUT_BYTES);
bits += (uint64_t)data_len << 3;
unsigned use = (unsigned)min((size_t)(SHA1_INPUT_BYTES - mlen), data_len);
memcpy(M + mlen, data, use);
mlen += use;
while (mlen == SHA1_INPUT_BYTES) {
data_len -= use;
data += use;
make_big_endian32((uint32_t*)M, SHA1_INPUT_WORDS);
transform();
use = (unsigned)min((size_t)SHA1_INPUT_BYTES, data_len);
memcpy(M, data, use);
mlen = use;
}
}
uint8_t* SHA1::result() {
unsigned mlen = (unsigned)((bits >> 3) % SHA1_INPUT_BYTES), padding = SHA1_INPUT_BYTES - mlen;
M[mlen++] = 0x80;
if (padding > BIT_COUNT_BYTES) {
memset(M + mlen, 0x00, padding - BIT_COUNT_BYTES);
make_big_endian32((uint32_t*)M, SHA1_INPUT_WORDS - BIT_COUNT_WORDS);
} else {
memset(M + mlen, 0x00, SHA1_INPUT_BYTES - mlen);
make_big_endian32((uint32_t*)M, SHA1_INPUT_WORDS);
transform();
memset(M, 0x00, SHA1_INPUT_BYTES - BIT_COUNT_BYTES);
}
uint64_t temp = littleEndian ? bits << 32 | bits >> 32 : bits;
memcpy(M + SHA1_INPUT_BYTES - BIT_COUNT_BYTES, &temp, BIT_COUNT_BYTES);
transform();
make_big_endian32(H, SHA1_DIGEST_WORDS);
return (uint8_t*)H;
}
template <typename T>
inline void axor(T* p1, const T* p2, size_t len) {
for (; len != 0; --len)
*p1++ ^= *p2++;
}
HMAC::HMAC(const uint8_t* key, size_t lkey) {
init(key, lkey);
}
void HMAC::init(const uint8_t* key, size_t lkey) {
in.init();
out.init();
uint8_t ipad[SHA1_INPUT_BYTES];
uint8_t opad[SHA1_INPUT_BYTES];
memset(ipad, 0x36, sizeof(ipad));
memset(opad, 0x5c, sizeof(opad));
if (lkey <= SHA1_INPUT_BYTES) {
axor(ipad, key, lkey);
axor(opad, key, lkey);
} else {
SHA1 tmp;
tmp.add(key, lkey);
const uint8_t* key2 = tmp.result();
axor(ipad, key2, SHA1_DIGEST_BYTES);
axor(opad, key2, SHA1_DIGEST_BYTES);
}
in.add((uint8_t*)ipad, sizeof(ipad));
out.add((uint8_t*)opad, sizeof(opad));
}
string GetDateString() {
time_t now_time;
time(&now_time);
if (AppConfig::GetInstance()->EnableLogTimeAutoAdjust()) {
now_time += GetTimeDelta();
}
char buffer[128] = {'\0'};
tm timeInfo;
#if defined(__linux__)
gmtime_r(&now_time, &timeInfo);
#elif defined(_MSC_VER)
gmtime_s(&timeInfo, &now_time);
#endif
strftime(buffer, 128, DATE_FORMAT_RFC822.c_str(), &timeInfo);
return string(buffer);
}
static bool StartWith(const std::string& input, const std::string& pattern) {
if (input.length() < pattern.length()) {
return false;
}
size_t i = 0;
while (i < pattern.length() && input[i] == pattern[i]) {
i++;
}
return i == pattern.length();
}
static std::string CalcSHA1(const std::string& message, const std::string& key) {
HMAC hmac(reinterpret_cast<const uint8_t*>(key.data()), key.size());
hmac.add(reinterpret_cast<const uint8_t*>(message.data()), message.size());
return string(reinterpret_cast<const char*>(hmac.result()), SHA1_DIGEST_BYTES);
}
string GetUrlSignature(const string& httpMethod,
const string& operationType,
map<string, string>& httpHeader,
const map<string, string>& parameterList,
const string& content,
const string& signKey) {
string contentMd5;
string signature;
string osstream;
if (!content.empty()) {
contentMd5 = CalcMD5(content);
}
string contentType;
map<string, string>::iterator iter = httpHeader.find(CONTENT_TYPE);
if (iter != httpHeader.end()) {
contentType = iter->second;
}
map<string, string> endingMap;
osstream.append(httpMethod);
osstream.append("\n");
osstream.append(contentMd5);
osstream.append("\n");
osstream.append(contentType);
osstream.append("\n");
osstream.append(httpHeader[DATE]);
osstream.append("\n");
for (map<string, string>::const_iterator iter = httpHeader.begin(); iter != httpHeader.end(); ++iter) {
if (StartWith(iter->first, LOG_OLD_HEADER_PREFIX)) {
string key = iter->first;
endingMap.insert(make_pair(key.replace(0, LOG_OLD_HEADER_PREFIX.size(), LOG_HEADER_PREFIX), iter->second));
} else if (StartWith(iter->first, LOG_HEADER_PREFIX) || StartWith(iter->first, ACS_HEADER_PREFIX)) {
endingMap.insert(make_pair(iter->first, iter->second));
}
}
for (map<string, string>::const_iterator it = endingMap.begin(); it != endingMap.end(); ++it) {
osstream.append(it->first);
osstream.append(":");
osstream.append(it->second);
osstream.append("\n");
}
osstream.append(operationType);
if (parameterList.size() > 0) {
osstream.append("?");
for (map<string, string>::const_iterator iter = parameterList.begin(); iter != parameterList.end(); ++iter) {
if (iter != parameterList.begin()) {
osstream.append("&");
}
osstream.append(iter->first);
osstream.append("=");
osstream.append(iter->second);
}
}
signature = Base64Enconde(CalcSHA1(osstream, signKey));
return signature;
}
} // namespace logtail