/** * Copyright (c) 2021 OceanBase * OceanBase Database Proxy(ODP) is licensed under Mulan PubL v2. * You can use this software according to the terms and conditions of the Mulan PubL v2. * You may obtain a copy of Mulan PubL v2 at: * http://license.coscl.org.cn/MulanPubL-2.0 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PubL v2 for more details. */ #define USING_LOG_PREFIX SHARE_SCHEMA #include "lib/encrypt/ob_encrypted_helper.h" #include #include #include #include "lib/oblog/ob_log.h" #include "lib/oblog/ob_log_module.h" using namespace oceanbase; using namespace oceanbase::common; const uint32_t ObEncryptedHelper::sha_const_key[5] = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }; const uint32_t ObEncryptedHelper::K[4] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 }; /* * When login, server sends random scramble str. * Client gets the scramble, use this logic to generate login rsp: * stage1 = sha1(passwd) * * stage1's size is 41B, contain '*' + octal num(40B) * it is readable for user * */ int ObEncryptedHelper::encrypt_passwd_to_stage1(const ObString &password, ObString &stage1) { int ret = OB_SUCCESS; SHA1_CONTEXT sha1_context; memset(static_cast(&sha1_context), 0, sizeof(SHA1_CONTEXT)); unsigned char hash_stage1[SHA1_HASH_SIZE] = {0}; if (OB_ISNULL(stage1.ptr())) { ret = OB_INVALID_ARGUMENT; LOG_WARN("stage1 not null", KP(stage1.ptr())); } else if (OB_UNLIKELY(stage1.length() < SCRAMBLE_LENGTH * 2 + 1)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("stage1 length too short", KP(stage1.ptr()), K(stage1.length()), K(ret)); } else { // sha1(passwd) => stage1 if (OB_FAIL(mysql_sha1_reset_wrap(&sha1_context))) { LOG_WARN("failed to reset sha context", K(ret)); } else if (OB_FAIL(mysql_sha1_input_wrap(&sha1_context, reinterpret_cast(password.ptr()), password.length()))) { LOG_WARN("failed to feed stage1 input", K(ret)); } else if (OB_FAIL(mysql_sha1_result_wrap(&sha1_context, hash_stage1))) { LOG_WARN("failed to get hash_stage1", K(ret)); } else { int64_t cnt = 0; char *out_buf = stage1.ptr(); //in mysql, 40-digit hash always begins with a '*' in order to distinguish from pre 4.1 hash out_buf[0] = '*'; for (int64_t i = 0; OB_SUCC(ret) && i < SHA1_HASH_SIZE; ++i) { cnt = snprintf(out_buf + 1 + i * 2, 3, "%02hhx", reinterpret_cast(&hash_stage1)[i]); if (2 != cnt) { ret = OB_ERR_UNEXPECTED; LOG_WARN("failed to snprintf, converted cnt != 2", K(cnt), K(ret)); } } LOG_DEBUG("succ to encrypt_passwd_to_stage1", K(stage1)); } } return ret; } /* * When login, server sends random scramble str. * Client gets the scramble, use this logic to generate login rsp: * stage1 = sha1(passwd) * stage2 = sha1(stage1) * * stage2's size is 41B, contain '*' + octal num(40B) * it is readable for user * */ int ObEncryptedHelper::encrypt_passwd_to_stage2(const ObString &password, ObString &mysql_stage2) { int ret = OB_SUCCESS; SHA1_CONTEXT sha1_context; memset(static_cast(&sha1_context), 0, sizeof(SHA1_CONTEXT)); unsigned char hash_stage1[SHA1_HASH_SIZE] = {0}; unsigned char hash_stage2[SHA1_HASH_SIZE] = {0}; if (OB_ISNULL(mysql_stage2.ptr())) { ret = OB_INVALID_ARGUMENT; LOG_WARN("mysql_stage2 not null", KP(mysql_stage2.ptr())); } else if (OB_UNLIKELY(mysql_stage2.length() < SCRAMBLE_LENGTH * 2 + 1)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("mysql_stage2 length too short", KP(mysql_stage2.ptr()), K(mysql_stage2.length()), K(ret)); } else { /* sha1(passwd) => stage1 * sha1(stage1) => stage2 * concat('*', stage2) => mysql_stage2 */ if (OB_FAIL(mysql_sha1_reset_wrap(&sha1_context))) { LOG_WARN("failed to reset sha context", K(ret)); } else if (OB_FAIL(mysql_sha1_input_wrap(&sha1_context, reinterpret_cast(password.ptr()), password.length()))) { LOG_WARN("failed to feed stage1 input", K(ret)); } else if (OB_FAIL(mysql_sha1_result_wrap(&sha1_context, hash_stage1))) { LOG_WARN("failed to get hash_stage1", K(ret)); } else if (OB_FAIL(mysql_sha1_reset_wrap(&sha1_context))) { LOG_WARN("failed to reset sha context", K(ret)); } else if (OB_FAIL(mysql_sha1_input_wrap(&sha1_context, hash_stage1, SHA1_HASH_SIZE))) { LOG_WARN("failed to feed stage2 input", K(ret)); } else if (OB_FAIL(mysql_sha1_result_wrap(&sha1_context, hash_stage2))) { LOG_WARN("failed to get hash_stage2", K(ret)); } else { int64_t cnt = 0; char *out_buf = mysql_stage2.ptr(); //in mysql, 40-digit hash always begins with a '*' in order to distinguish from pre 4.1 hash out_buf[0] = '*'; for (int64_t i = 0; OB_SUCC(ret) && i < SHA1_HASH_SIZE; ++i) { cnt = snprintf(out_buf + 1 + i * 2, 3, "%02hhx", reinterpret_cast(&hash_stage2)[i]); if (2 != cnt) { ret = OB_ERR_UNEXPECTED; LOG_WARN("failed to snprintf, converted cnt != 2", K(cnt), K(ret)); } } } } return ret; } /* * stage1's size is 41B, contain '*' + octal num(40B) * it is readable for user * * stage2_hex's size is 20B, contain hex num(20B) * it is unreadable for user * */ int ObEncryptedHelper::encrypt_stage1_to_stage2_hex(const ObString &stage1, char *stage2_hex_buf, const int64_t buf_len, int64_t ©_len) { int ret = OB_SUCCESS; SHA1_CONTEXT sha1_context; memset(static_cast(&sha1_context), 0, sizeof(SHA1_CONTEXT)); if (OB_ISNULL(stage1.ptr()) || OB_UNLIKELY(stage1.length() != SCRAMBLE_LENGTH * 2)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("stage1 is invalid", KP(stage1.ptr()), KP(stage1.length())); } else if (OB_ISNULL(stage2_hex_buf) || OB_UNLIKELY(buf_len != SCRAMBLE_LENGTH)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("stage2 is invalid", KP(stage2_hex_buf), K(buf_len), K(ret)); } else { //stage1 ==> stage1_hex char stage1_hex[SCRAMBLE_LENGTH] = {0}; ObString stage1_hex_str(SCRAMBLE_LENGTH, stage1_hex); if (OB_FAIL(displayable_to_hex(stage1, stage1_hex_str))) { LOG_WARN("fail to displayable_to_hex", K(stage1), K(ret)); } // sha1(stage1_hex) => stage2 if (FAILEDx(mysql_sha1_reset_wrap(&sha1_context))) { LOG_WARN("failed to reset sha context", K(ret)); } else if (OB_FAIL(mysql_sha1_input_wrap(&sha1_context, reinterpret_cast(stage1_hex_str.ptr()), stage1_hex_str.length()))) { LOG_WARN("failed to feed stage1 input", K(ret)); } else if (OB_FAIL(mysql_sha1_result_wrap(&sha1_context, reinterpret_cast(stage2_hex_buf)))) { LOG_WARN("failed to get hash_stage1", K(ret)); } else { copy_len = SCRAMBLE_LENGTH; } } return ret; } /* * MySQL validation logic: * Server stores the 'stage2' hash, which is cleartext password calc-ed sha1 twice. * When login, server sends random scramble str. * Client gets the scramble, use this logic to generate login rsp: * stage1 = sha1(passwd) * stage2 = sha1(stage1) * scrambled_stage2 = sha1(scramble_got_from_server, stage2) * login_rsp = xor(stage1, scrambled_stage2) * **/ int ObEncryptedHelper::encrypt_password(const ObString &raw_pwd, const ObString &scramble_str, char *pwd_buf, const int64_t buf_len, int64_t ©_len) { int ret = OB_SUCCESS; copy_len = 0; if (raw_pwd.empty()) { LOG_INFO("empty password"); } else if (OB_UNLIKELY(scramble_str.empty()) || OB_UNLIKELY(SCRAMBLE_LENGTH != scramble_str.length()) || OB_ISNULL(pwd_buf) || (buf_len <= SHA1_HASH_SIZE)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid input value", K(raw_pwd), K(scramble_str), KP(pwd_buf), K(buf_len), K(ret)); } else { SHA1_CONTEXT sha1_context; MEMSET(static_cast(&sha1_context), 0, sizeof(SHA1_CONTEXT)); unsigned char stage1_client[SHA1_HASH_SIZE] = {0}; // 1. stage1 = sha1(passwd) if (OB_FAIL(mysql_sha1_reset_wrap(&sha1_context))) { LOG_WARN("failed to reset sha context", K(ret)); } else if (OB_FAIL(mysql_sha1_input_wrap(&sha1_context, reinterpret_cast(raw_pwd.ptr()), raw_pwd.length()))) { LOG_WARN("failed to feed raw_pwd input", K(ret)); } else if (OB_FAIL(mysql_sha1_result_wrap(&sha1_context, stage1_client))) { LOG_WARN("failed to get stage1_client", K(ret)); } // 2. stage2 = sha1(stage1) if (OB_SUCC(ret)) { const ObString stage1_str(SHA1_HASH_SIZE, reinterpret_cast(stage1_client)); if (OB_FAIL(encrypt_stage1_hex(stage1_str, scramble_str, pwd_buf, buf_len, copy_len))) { LOG_WARN("failed to encrypt_stage1", K(ret)); } } } return ret; } /* * MySQL validation logic: * Server stores the 'stage2' hash, which is cleartext password calc-ed sha1 twice. * When login, server sends random scramble str. * Client gets the scramble, use this logic to generate login rsp: * stage1 = sha1(passwd) * stage2 = sha1(stage1_hex) * scrambled_stage2 = sha1(scramble_got_from_server, stage2_hex) * login_rsp = xor(stage1_hex, scrambled_stage2) * * stage1_hex's size is 20B, contain hex num(20B) * it is unreadable for user **/ int ObEncryptedHelper::encrypt_stage1_hex(const ObString &stage1_hex_str, const ObString &scramble_str, char *pwd_buf, const int64_t buf_len, int64_t ©_len) { int ret = OB_SUCCESS; copy_len = 0; if (OB_UNLIKELY(scramble_str.empty()) || OB_UNLIKELY(SCRAMBLE_LENGTH != scramble_str.length()) || OB_ISNULL(pwd_buf) || OB_UNLIKELY(buf_len <= SHA1_HASH_SIZE) || OB_ISNULL(stage1_hex_str.ptr()) || OB_UNLIKELY(stage1_hex_str.length() != SHA1_HASH_SIZE)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid input value", K(stage1_hex_str.ptr()), K(stage1_hex_str.length()), K(scramble_str), KP(pwd_buf), K(buf_len), K(ret)); } else { SHA1_CONTEXT sha1_context; MEMSET(static_cast(&sha1_context), 0, sizeof(SHA1_CONTEXT)); unsigned char stage2_client[SHA1_HASH_SIZE] = {0}; unsigned char stage2_scambled[SHA1_HASH_SIZE] = {0}; // 1. stage2 = sha1(stage1) if (OB_SUCC(ret)) { if (OB_FAIL(mysql_sha1_reset_wrap(&sha1_context))) { LOG_WARN("failed to reset sha context", K(ret)); } else if (OB_FAIL(mysql_sha1_input_wrap(&sha1_context, reinterpret_cast(stage1_hex_str.ptr()), SHA1_HASH_SIZE))) { LOG_WARN("failed to feed stage1_client input", K(ret)); } else if (OB_FAIL(mysql_sha1_result_wrap(&sha1_context, stage2_client))) { LOG_WARN("failed to get stage2_client", K(ret)); } } // 2. scrambled_stage2 = sha1(scramble_got_from_server, stage2) if (OB_SUCC(ret)) { if (OB_FAIL(mysql_sha1_reset_wrap(&sha1_context))) { LOG_WARN("failed to reset sha context", K(ret)); } else if (OB_FAIL(mysql_sha1_input_wrap(&sha1_context, reinterpret_cast(scramble_str.ptr()), SCRAMBLE_LENGTH))) { LOG_WARN("failed to feed scramble_str input", K(ret)); } else if (OB_FAIL(mysql_sha1_input_wrap(&sha1_context, stage2_client, SHA1_HASH_SIZE))) { LOG_WARN("failed to feed stage2_client input", K(ret)); } else if (OB_FAIL(mysql_sha1_result_wrap(&sha1_context, stage2_scambled))) { LOG_WARN("failed to get stage2_scambled", K(ret)); } } // 3. login_rsp = xor(stage1, scrambled_stage2) if (OB_SUCC(ret)) { if (OB_FAIL(my_xor(reinterpret_cast(stage1_hex_str.ptr()), stage2_scambled, SHA1_HASH_SIZE, reinterpret_cast(pwd_buf)))) { LOG_WARN("failed to calc xor", K(ret)); } else { copy_len = SHA1_HASH_SIZE; } } } return ret; } int ObEncryptedHelper::check_login(const ObString &login_reply, const ObString &scramble_str, const ObString &stored_stage2, bool &pass) { int ret = OB_SUCCESS; SHA1_CONTEXT sha1_context; memset(static_cast(&sha1_context), 0, sizeof(SHA1_CONTEXT)); unsigned char stage2_scambled[SHA1_HASH_SIZE] = {0}; unsigned char stage1_client[SHA1_HASH_SIZE] = {0}; unsigned char stage2_client[SHA1_HASH_SIZE] = {0}; /* * MySQL validation logic: * Server stores the 'stage2' hash, which is cleartext password calc-ed sha1 twice. * When login, server sends random scramble str. * Client gets the scramble, use this logic to generate login rsp: * stage1 = sha1(passwd) * stage2 = sha1(stage1) * scrambled_stage2 = sha1(scramble_got_from_server, stage2) * login_rsp = xor(stage1, scrambled_stage2) * When server gets rsp, use this logic to check login: * scrambled_stage2_server = sha1(scramble_got_from_server, stage2_stored) * stage1_client_rebuilt = xor(login_rsp, scrambled_stage2_server) * stage2_client_rebuilt = sha1(stage1_client_rebuilt) * if (stage2_client_rebuilt == stage2_stored) { * pass * } else { * die * } * */ if (NULL == scramble_str.ptr() || scramble_str.length() < SCRAMBLE_LENGTH) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid scramble_str", KP(scramble_str.ptr()), K(scramble_str.length())); } else if (NULL == stored_stage2.ptr() || stored_stage2.length() != SHA1_HASH_SIZE) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid stored_stage2", KP(stored_stage2.ptr()), K(stored_stage2.length())); } else if (NULL == login_reply.ptr() || login_reply.length() != SHA1_HASH_SIZE) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid login_reply", KP(login_reply.ptr()), K(login_reply.length())); } else { //scample the stored stage2 hash with scramble_str if (OB_FAIL(mysql_sha1_reset_wrap(&sha1_context))) { LOG_WARN("failed to reset sha context", K(ret)); } else if (OB_FAIL(mysql_sha1_input_wrap(&sha1_context, reinterpret_cast(scramble_str.ptr()), SCRAMBLE_LENGTH))) { LOG_WARN("failed to feed scramble_str input", K(ret)); } else if (OB_FAIL(mysql_sha1_input_wrap(&sha1_context, reinterpret_cast(stored_stage2.ptr()), SHA1_HASH_SIZE))) { LOG_WARN("failed to feed stored_stage2 input", K(ret)); } else if (OB_FAIL(mysql_sha1_result_wrap(&sha1_context, stage2_scambled))) { LOG_WARN("failed to get stage2_scambled", K(ret)); } else if (OB_FAIL(my_xor(reinterpret_cast(login_reply.ptr()), stage2_scambled, SHA1_HASH_SIZE, stage1_client))) { LOG_WARN("failed to calc xor", K(ret)); } //reproduce stage2 of client, compare it to stored_stage2 if (OB_SUCC(ret)) { if (OB_FAIL(mysql_sha1_reset_wrap(&sha1_context))) { LOG_WARN("failed to reset sha context", K(ret)); } else if (OB_FAIL(mysql_sha1_input_wrap(&sha1_context, stage1_client, SCRAMBLE_LENGTH))) { LOG_WARN("failed to feed stage1_client input", K(ret)); } else if (OB_FAIL(mysql_sha1_result_wrap(&sha1_context, stage2_client))) { LOG_WARN("failed to get stage2_client", K(ret)); } else { pass = (0 == MEMCMP(stage2_client, stored_stage2.ptr(), SHA1_HASH_SIZE)); } } } return ret; } int ObEncryptedHelper::displayable_to_hex(const ObString &displayable, ObString &hex) { int ret = OB_SUCCESS; const char * in_buf = displayable.ptr(); char * out_buf = hex.ptr(); if (NULL == in_buf || displayable.length() < SHA1_HASH_SIZE * 2) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid displayable string", KP(in_buf), K(displayable.length()), K(ret)); } else if (NULL == out_buf || hex.length() < SHA1_HASH_SIZE) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid hex buf", KP(in_buf), K(displayable.length()), K(ret)); } else { for (int64_t i = 0; OB_SUCC(ret) && i < SHA1_HASH_SIZE; ++i) { int64_t high = 0; int64_t low = 0; if (OB_FAIL(char_to_hex(in_buf[i * 2], high))) { LOG_WARN("Failed convert high char to hex", K(ret)); } else if (OB_FAIL(char_to_hex(in_buf[i * 2 + 1], low))) { LOG_WARN("Failed convert low char to hex", K(ret)); } else { out_buf[i] = static_cast(high << 4 | low); } } } return ret; } // RPC 3174 void ObEncryptedHelper::SHA1ProcessMessageBlock(SHA1_CONTEXT *context) { int t = 0; /* Loop counter */ uint32_t temp = 0; /* Temporary word value */ uint32_t W[80]; /* Word sequence */ uint32_t A = 0, B = 0, C = 0, D = 0, E = 0; /* Word buffers */ int idx = 0; for (t = 0; t < 16; t++) { idx = t * 4; W[t] = context->Message_Block[idx] << 24; W[t] |= context->Message_Block[idx + 1] << 16; W[t] |= context->Message_Block[idx + 2] << 8; W[t] |= context->Message_Block[idx + 3]; } for (t = 16; t < 80; t++) { W[t] = SHA1CircularShift(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]); } A = context->Intermediate_Hash[0]; B = context->Intermediate_Hash[1]; C = context->Intermediate_Hash[2]; D = context->Intermediate_Hash[3]; E = context->Intermediate_Hash[4]; for (t = 0; t < 20; t++) { temp = SHA1CircularShift(5, A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; E = D; D = C; C = SHA1CircularShift(30, B); B = A; A = temp; } for (t = 20; t < 40; t++) { temp = SHA1CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[1]; E = D; D = C; C = SHA1CircularShift(30, B); B = A; A = temp; } for (t = 40; t < 60; t++) { temp = (SHA1CircularShift(5, A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]); E = D; D = C; C = SHA1CircularShift(30, B); B = A; A = temp; } for (t = 60; t < 80; t++) { temp = SHA1CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[3]; E = D; D = C; C = SHA1CircularShift(30, B); B = A; A = temp; } context->Intermediate_Hash[0] += A; context->Intermediate_Hash[1] += B; context->Intermediate_Hash[2] += C; context->Intermediate_Hash[3] += D; context->Intermediate_Hash[4] += E; context->Message_Block_Index = 0; } /* Pad message SYNOPSIS SHA1PadMessage() context: [in/out] The context to pad DESCRIPTION According to the standard, the message must be padded to an even 512 bits. The first padding bit must be a '1'. The last 64 bits represent the length of the original message. All bits in between should be 0. This function will pad the message according to those rules by filling the Message_Block array accordingly. It will also call the ProcessMessageBlock function provided appropriately. When it returns, it can be assumed that the message digest has been computed. */ void ObEncryptedHelper::SHA1PadMessage(SHA1_CONTEXT *context) { /* Check to see if the current message block is too small to hold the initial padding bits and length. If so, we will pad the block, process it, and then continue padding into a second block. */ int i = context->Message_Block_Index; if (i > 55) { context->Message_Block[i++] = 0x80; memset((char *) &context->Message_Block[i], 0, sizeof(context->Message_Block[0]) * (64 - i)); context->Message_Block_Index = 64; /* This function sets context->Message_Block_Index to zero */ SHA1ProcessMessageBlock(context); memset((char *) &context->Message_Block[0], 0, sizeof(context->Message_Block[0]) * 56); context->Message_Block_Index = 56; } else { context->Message_Block[i++] = 0x80; memset((char *) &context->Message_Block[i], 0, sizeof(context->Message_Block[0]) * (56 - i)); context->Message_Block_Index = 56; } /* Store the message length as the last 8 octets */ context->Message_Block[56] = (int8_t)(context->Length >> 56); context->Message_Block[57] = (int8_t)(context->Length >> 48); context->Message_Block[58] = (int8_t)(context->Length >> 40); context->Message_Block[59] = (int8_t)(context->Length >> 32); context->Message_Block[60] = (int8_t)(context->Length >> 24); context->Message_Block[61] = (int8_t)(context->Length >> 16); context->Message_Block[62] = (int8_t)(context->Length >> 8); context->Message_Block[63] = (int8_t)(context->Length); SHA1ProcessMessageBlock(context); } /* Initialize SHA1Context SYNOPSIS mysql_sha1_reset() context [in/out] The context to reset. DESCRIPTION This function will initialize the SHA1Context in preparation for computing a new SHA1 message digest. RETURN SHA_SUCCESS ok != SHA_SUCCESS sha Error Code. */ int ObEncryptedHelper::mysql_sha1_reset(SHA1_CONTEXT *context) { context->Length = 0; context->Message_Block_Index = 0; context->Intermediate_Hash[0] = sha_const_key[0]; context->Intermediate_Hash[1] = sha_const_key[1]; context->Intermediate_Hash[2] = sha_const_key[2]; context->Intermediate_Hash[3] = sha_const_key[3]; context->Intermediate_Hash[4] = sha_const_key[4]; context->Computed = 0; context->Corrupted = 0; return SHA_SUCCESS; } int ObEncryptedHelper::mysql_sha1_reset_wrap(SHA1_CONTEXT *context) { int ret = OB_SUCCESS; if (OB_ISNULL(context)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("context is null", KP(context), K(ret)); } else { int sha_ret = SHA_SUCCESS; if (SHA_SUCCESS != (sha_ret = mysql_sha1_reset(context))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("failed to reset sha", K(sha_ret), K(ret)); } } return ret; } /* Return the 160-bit message digest into the array provided by the caller SYNOPSIS mysql_sha1_result() context [in/out] The context to use to calculate the SHA-1 hash. Message_Digest: [out] Where the digest is returned. DESCRIPTION NOTE: The first octet of hash is stored in the 0th element, the last octet of hash in the 19th element. RETURN SHA_SUCCESS ok != SHA_SUCCESS sha Error Code. */ int ObEncryptedHelper::mysql_sha1_result(SHA1_CONTEXT *context, unsigned char Message_Digest[SHA1_HASH_SIZE]) { int i = 0; if (!context->Computed) { SHA1PadMessage(context); /* message may be sensitive, clear it out */ memset((char *) context->Message_Block, 0, 64); context->Length = 0; /* and clear length */ context->Computed = 1; } for (i = 0; i < SHA1_HASH_SIZE; i++) Message_Digest[i] = (int8_t)((context->Intermediate_Hash[i >> 2] >> 8 * (3 - (i & 0x03)))); return SHA_SUCCESS; } int ObEncryptedHelper::mysql_sha1_result_wrap(SHA1_CONTEXT *context, unsigned char *message_digest) { int ret = OB_SUCCESS; if (OB_ISNULL(context)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("context is null", KP(context), K(ret)); } else if (OB_ISNULL(message_digest)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("message_digest is null", KP(message_digest), K(ret)); } else { int sha_ret = SHA_SUCCESS; if (SHA_SUCCESS != (sha_ret = mysql_sha1_result(context, message_digest))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("failed to get sha result", K(sha_ret), K(ret)); } } return ret; } /* Accepts an array of octets as the next portion of the message. SYNOPSIS mysql_sha1_input() context [in/out] The SHA context to update message_array An array of characters representing the next portion of the message. length The length of the message in message_array RETURN SHA_SUCCESS ok != SHA_SUCCESS sha Error Code. */ int ObEncryptedHelper::mysql_sha1_input(SHA1_CONTEXT *context, const unsigned char *message_array, unsigned length) { int sha_ret = SHA_SUCCESS; if (!length) { sha_ret = SHA_SUCCESS; } else if (!context || !message_array) { /* We assume client knows what it is doing in non-debug mode */ sha_ret = SHA_NULL; } else if (!!context->Computed) { sha_ret = (context->Corrupted = SHA_STATE_ERROR); } else if (!!context->Corrupted) { sha_ret = context->Corrupted; } else { while (SHA_SUCCESS == sha_ret && length--) { context->Message_Block[context->Message_Block_Index++] = (*message_array & 0xFF); context->Length += 8; /* Length is in bits */ /* Then we're not debugging we assume we never will get message longer 2^64 bits. */ if (context->Length == 0) { sha_ret = (context->Corrupted = 1); /* Message is too long */ } else { if (context->Message_Block_Index == 64) { SHA1ProcessMessageBlock(context); } message_array++; } } } return sha_ret; } int ObEncryptedHelper::mysql_sha1_input_wrap(SHA1_CONTEXT *context, const unsigned char *message_array, const ObString::obstr_size_t length) { int ret = OB_SUCCESS; if (OB_ISNULL(context)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("context is null", KP(context), K(ret)); } else if (OB_ISNULL(message_array)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("message_array is null", KP(message_array), K(ret)); } else { int sha_ret = SHA_SUCCESS; if (SHA_SUCCESS != (sha_ret = mysql_sha1_input(context, message_array, static_cast(length)))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("failed to feed sha input", K(sha_ret), K(ret)); } } return ret; } int ObEncryptedHelper::my_xor(const unsigned char *s1, const unsigned char *s2, uint32_t len, unsigned char *to) { int ret = OB_SUCCESS; if (OB_ISNULL(s1) || OB_ISNULL(s2) || OB_ISNULL(to)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("input is null", KP(s1), KP(s2)); } else { const unsigned char *s1_end = s1 + len; while (s1 < s1_end) { *to++ = *s1++ ^ *s2++; } } return ret; } int ObEncryptedHelper::char_to_hex(char input, int64_t &out) { int ret = OB_SUCCESS; if (!((input >= '0' && input <= '9') || (input >= 'a' && input <= 'f') || (input >= 'A' && input <= 'F'))) { ret = OB_INVALID_ARGUMENT; LOG_WARN("input not in range", K(input)); } else { input = static_cast(tolower(input)); if (input >= '0' && input <= '9') { out = static_cast(input - '0'); } else if (input >= 'a' && input <= 'f') { out = static_cast(input - 'a' + 10); } } return ret; }