You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
305 lines
9.6 KiB
305 lines
9.6 KiB
/*
|
|
Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with MySQL.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License, version 2.0, for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "converter.h"
|
|
#include <string.h>
|
|
#include <iostream>
|
|
|
|
namespace keyring {
|
|
|
|
const size_t WORD_32 = 4; //<! number of bytes in 32 bit word
|
|
const size_t WORD_64 = 8; //<! number of bytes in 64 bit word
|
|
const size_t LENGTHS_COUNT = 5; //<! number of length values in serialization
|
|
|
|
// determine native architecture at startup
|
|
const Converter::Arch Converter::native_arch{Converter::detect_native_arch()};
|
|
|
|
/**
|
|
fetches native machine architecture of server binary
|
|
|
|
@return - native machine architecture
|
|
*/
|
|
Converter::Arch Converter::get_native_arch() { return native_arch; }
|
|
|
|
/**
|
|
(static) determines width of architecture word in bytes
|
|
|
|
@param arch - architecture to return word width of
|
|
|
|
@return
|
|
@retval > 0 - word width of the architecture
|
|
@retval = 0 - architecture has no known word width
|
|
*/
|
|
size_t Converter::get_width(Arch arch) {
|
|
if (arch == Arch::LE_64 || arch == Arch::BE_64) return WORD_64;
|
|
if (arch == Arch::LE_32 || arch == Arch::BE_32) return WORD_32;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
(static) calculates native unsigned integer (size_t) value of binary buffer
|
|
- buffer has to provide for at least sizeof(size_t) bytes
|
|
|
|
@param length - pointer to native binary representation of unsigned value
|
|
|
|
@return - unsigned integer (size_t) value of binary buffer value
|
|
*/
|
|
size_t Converter::native_value(char const *binary_value) {
|
|
// overwrite variable with bytes from binary buffer
|
|
size_t real_value = 0;
|
|
memcpy(&real_value, binary_value, sizeof(size_t));
|
|
return real_value;
|
|
}
|
|
|
|
/**
|
|
(static) converts serialized key data from one architecture to another
|
|
- key lengths word widths are expanded/contracted, key data just copied
|
|
|
|
@param (I) data - buffer holding key data to be converted
|
|
@param (I) data_size - size of the key data within buffer
|
|
@param (I) src - machine architecture of the input key data
|
|
@param (I) dst - machine architecture of the output key data
|
|
@param (O) out - output key data
|
|
|
|
@return
|
|
@retval false - data was successfully converted
|
|
@retval true - error occurred during conversion
|
|
*/
|
|
bool Converter::convert_data(char const *data, size_t data_size, Arch src,
|
|
Arch dst, std::string &out) {
|
|
// at least source or destination has to be native
|
|
if (src != native_arch && dst != native_arch) return true;
|
|
|
|
// no data - we can convert that :)
|
|
if (data_size == 0) {
|
|
out = std::string();
|
|
return false;
|
|
}
|
|
|
|
// no need to convert between identical architectures
|
|
if (src == dst) {
|
|
out = std::string(data, data_size);
|
|
return false;
|
|
}
|
|
|
|
// get word width of source and destination architecture
|
|
auto src_width = get_width(src);
|
|
auto dst_width = get_width(dst);
|
|
|
|
size_t loc = 0;
|
|
std::string output;
|
|
char number[WORD_64] = {0};
|
|
size_t lengths[LENGTHS_COUNT] = {0};
|
|
std::string key_content;
|
|
|
|
// load remaining keys and convert them
|
|
while (data_size >= loc + LENGTHS_COUNT * src_width) {
|
|
key_content.clear();
|
|
|
|
// load file length values, convert them and append
|
|
for (size_t i = 0; i < LENGTHS_COUNT; i++) {
|
|
// convert value to different architecture
|
|
size_t converted_width = convert(data + loc, number, src, dst);
|
|
if (i > 0) key_content.append(number, converted_width);
|
|
|
|
// determine length integer value
|
|
if (src == get_native_arch())
|
|
lengths[i] = native_value(data + loc);
|
|
else
|
|
lengths[i] = native_value(number);
|
|
|
|
// move to next length
|
|
loc += src_width;
|
|
}
|
|
|
|
// real size without padding has to be smaller that total size
|
|
size_t real_size = lengths[1] + lengths[2] + lengths[3] + lengths[4];
|
|
if (lengths[0] < real_size) return true;
|
|
|
|
// we also have to have at least remaining key data
|
|
if (loc + lengths[0] - LENGTHS_COUNT * src_width > data_size) return true;
|
|
|
|
// append only key data without padding
|
|
key_content.append(data + loc, real_size);
|
|
loc += lengths[0] - LENGTHS_COUNT * src_width;
|
|
|
|
// append required padding to destination
|
|
auto total = LENGTHS_COUNT * dst_width + real_size;
|
|
size_t padding = (dst_width - total % dst_width) % dst_width;
|
|
key_content.append(padding, '\0');
|
|
|
|
// new key package size is waranted
|
|
lengths[0] = total + padding;
|
|
char tmp_buffer[WORD_64];
|
|
memcpy(tmp_buffer, lengths, sizeof(size_t));
|
|
|
|
// conversion may be necessary
|
|
if (dst != get_native_arch()) {
|
|
auto converted_width = convert(tmp_buffer, number, src, dst);
|
|
output += std::string(number, converted_width);
|
|
output += key_content;
|
|
} else {
|
|
output += std::string(tmp_buffer, sizeof(size_t));
|
|
output += key_content;
|
|
}
|
|
}
|
|
|
|
// we must've taken all keys
|
|
if (loc != data_size) return true;
|
|
|
|
// return converted data buffer
|
|
out = output;
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
(static) converts binary length representation between architecture types
|
|
|
|
@param (I) src - input binary representation
|
|
@param (O) dst - output binary representation
|
|
@param (I) src_t - architecture type of input representation
|
|
@param (I) dst_t - architecture type of output representation
|
|
|
|
@return
|
|
@retval > 0 - size of destination representation
|
|
@retval = 0 - conversion was not possible
|
|
|
|
*/
|
|
size_t Converter::convert(char const *src, char *dst, Arch src_t, Arch dst_t) {
|
|
// source and destination type should be known
|
|
if (src_t == Arch::UNKNOWN || dst_t == Arch::UNKNOWN) return 0;
|
|
|
|
// find out source and destination characteristics
|
|
const size_t src_width = get_width(src_t);
|
|
const size_t dst_width = get_width(dst_t);
|
|
const bool src_is_le = get_endian(src_t) == Endian::LITTLE ? 1 : 0;
|
|
const bool dst_is_le = get_endian(dst_t) == Endian::LITTLE ? 1 : 0;
|
|
|
|
// determine required operations
|
|
const bool swap = (src_is_le != dst_is_le);
|
|
const bool grow = (src_width < dst_width);
|
|
const bool crop = (src_width > dst_width);
|
|
|
|
// crop security - we mustn't lose precision
|
|
if (crop) {
|
|
if (src_is_le) // for little endian ending bytes must be zero
|
|
{
|
|
if (src[4] | src[5] | src[6] | src[7]) return 0;
|
|
} else // for big endian leading bytes must be zero
|
|
{
|
|
if (src[0] | src[1] | src[2] | src[3]) return 0;
|
|
}
|
|
}
|
|
|
|
// if needed, prepare reversed number
|
|
char swapped_src[WORD_64] = {0};
|
|
if (swap) {
|
|
// copy reversed byte order
|
|
for (size_t i = 0; i < src_width; i++)
|
|
swapped_src[i] = src[src_width - i - 1];
|
|
|
|
// use reversed representation instead of original
|
|
src = swapped_src;
|
|
}
|
|
|
|
// ================== CASE 1 - no change in bit width ================== //
|
|
if (!grow && !crop) {
|
|
memcpy(dst, src, dst_width);
|
|
}
|
|
// ================== CASE 2 - grow bit width ========================== //
|
|
else if (grow) {
|
|
if (dst_is_le) {
|
|
// copy to starting bytes, clear upper bytes
|
|
memcpy(dst, src, src_width);
|
|
memset(dst + src_width, 0, dst_width - src_width);
|
|
} else {
|
|
// clear starting bytes, copy to upper bytes
|
|
memset(dst, 0, dst_width - src_width);
|
|
memcpy(dst + dst_width - src_width, src, src_width);
|
|
}
|
|
}
|
|
// ================== CASE 3 - crop bit width ========================== //
|
|
else if (crop) {
|
|
if (dst_is_le)
|
|
memcpy(dst, src, dst_width);
|
|
else
|
|
memcpy(dst, src + 4, dst_width);
|
|
}
|
|
|
|
// we're returning size of output binary representation
|
|
return dst_width;
|
|
}
|
|
|
|
// =========================== PRIVATE UTILITIES ============================ //
|
|
|
|
/**
|
|
(static) detects native architecture type of machine server is running on
|
|
- to be called only at server startup
|
|
|
|
@return - native architecture of machine
|
|
*/
|
|
Converter::Arch Converter::detect_native_arch() {
|
|
auto type = Arch::UNKNOWN;
|
|
|
|
// determine bit width
|
|
const size_t bit_width = 8 * sizeof(size_t);
|
|
|
|
// determine endianess
|
|
size_t number = 1;
|
|
bool isLittleEndian = *(char *)(&number);
|
|
|
|
// assign architecture type based on findings
|
|
switch (bit_width) {
|
|
case 32:
|
|
if (isLittleEndian)
|
|
type = Arch::LE_32;
|
|
else
|
|
type = Arch::BE_32;
|
|
break;
|
|
|
|
case 64:
|
|
if (isLittleEndian)
|
|
type = Arch::LE_64;
|
|
else
|
|
type = Arch::BE_64;
|
|
break;
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
/**
|
|
(static) determines endianess of architecture
|
|
|
|
@param arch - architecture to return endianess of
|
|
|
|
@return - endianess of the architecture
|
|
*/
|
|
Converter::Endian Converter::get_endian(Arch arch) {
|
|
if (arch == Arch::LE_64 || arch == Arch::LE_32) return Endian::LITTLE;
|
|
if (arch == Arch::BE_64 || arch == Arch::BE_32) return Endian::BIG;
|
|
return Endian::UNKNOWN;
|
|
}
|
|
|
|
} /* namespace keyring */
|
|
|