/*
 * Copyright (c) 2010 SURFnet bv
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*****************************************************************************
 ByteString.cpp

 A string class for byte strings stored in securely allocated memory
 *****************************************************************************/

#include <algorithm>
#include <string>
#include <stdio.h>
#include "config.h"
#include "log.h"
#include "ByteString.h"

/**
 * Backward compatible fix: byte_str()/const_byte_str() to
 * return a non-NULL pointer, even if size() == 0, and yet
 * be compatible with -Wp,-D_GLIBCXX_ASSERTIONS stricter
 * bounds checking
 */
static unsigned char sentinel[1];

// Constructors
ByteString::ByteString()
{
}

ByteString::ByteString(const unsigned char* bytes, const size_t bytesLen)
{
	byteString.resize(bytesLen);

	if (bytesLen > 0)
		memcpy(&byteString[0], bytes, bytesLen);
}

ByteString::ByteString(const char* hexString)
{
	std::string hex = std::string(hexString);

	if (hex.size() % 2 != 0)
	{
		hex = "0" + hex;
	}

	for (size_t i = 0; i < hex.size(); i += 2)
	{
		std::string byteStr;
		byteStr += hex[i];
		byteStr += hex[i+1];

		unsigned char byteVal = (unsigned char) strtoul(byteStr.c_str(), NULL, 16);

		this->operator+=(byteVal);
	}
}

ByteString::ByteString(const unsigned long longValue)
{
	unsigned long setValue = longValue;

	// Convert the value to a big-endian byte string; N.B.: this code assumes that unsigned long
	// values are stored as a 64-bit value, which is a safe assumption on modern systems. It will
	// also properly handle a 32-bit value and will simply store 4 zeroes at the front of the
	// string. If at some point in time we get 128-bit architectures, the top 8 bytes of the value
	// will be discarded... (but hey, 640K is enough for everybody, right?)
	//
	// The reason for coding it this way is that implementations of SoftHSM will maintain
	// binary compatibility between eachothers background storage (i.e. a 32-bit SoftHSM version can
	// read the storage of a 64-bit version and vice versa under the assumption that the stored
	// values never exceed 32-bits, which is likely since these values are only used to encode
	// byte string lengths)
	unsigned char byteStrIn[8];
	
	for (size_t i = 0; i < 8; i++)
	{
		byteStrIn[7-i] = (unsigned char) (setValue & 0xFF);
		setValue >>= 8;
	}

	byteString.resize(8);
	memcpy(&byteString[0], byteStrIn, 8);
}

ByteString::ByteString(const ByteString& in)
{
	this->byteString = in.byteString;
}

// Assignment
ByteString& ByteString::operator=(const ByteString& in)
{
	this->byteString = in.byteString;
	return *this;
}

// Append data
ByteString& ByteString::operator+=(const ByteString& append)
{
	size_t curLen = byteString.size();
	size_t toAdd = append.byteString.size();
	size_t newLen = curLen + toAdd;

	byteString.resize(newLen);

	if (toAdd > 0)
		memcpy(&byteString[curLen], &append.byteString[0], toAdd);

	return *this;
}

ByteString& ByteString::operator+=(const unsigned char byte)
{
	byteString.push_back(byte);

	return *this;
}

// XORing
ByteString& ByteString::operator^=(const ByteString& rhs)
{
	size_t xorLen = std::min(this->size(), rhs.size());

	for (size_t i = 0; i < xorLen; i++)
	{
		byteString[i] ^= rhs.const_byte_str()[i];
	}

	return *this;
}

// Return a substring
ByteString ByteString::substr(const size_t start, const size_t len /* = SIZE_T_MAX */) const
{
	size_t retLen = std::min(len, byteString.size() - start);

	if (start >= byteString.size())
	{
		return ByteString();
	}
	else
	{
		return ByteString(&byteString[start], retLen);
	}
}

// Add data
ByteString operator+(const ByteString& lhs, const ByteString& rhs)
{
	ByteString rv = lhs;
	rv += rhs;

	return rv;
}

ByteString operator+(const unsigned char lhs, const ByteString& rhs)
{
	ByteString rv(&lhs, 1);
	rv += rhs;

	return rv;
}

ByteString operator+(const ByteString& lhs, const unsigned char rhs)
{
	ByteString rv = lhs;
	rv += rhs;

	return rv;
}

// Array operator
unsigned char& ByteString::operator[](size_t pos)
{
	return byteString[pos];
}

// Return the byte string data
unsigned char* ByteString::byte_str()
{
	if (byteString.size() != 0) {
		return &byteString[0];
	} else {
		return (unsigned char*) sentinel;
	}
}

// Return the const byte string
const unsigned char* ByteString::const_byte_str() const
{
	if (byteString.size() != 0) {
		return (const unsigned char*) &byteString[0];
	} else {
		return (const unsigned char*) sentinel;
	}
}

// Return a hexadecimal character representation of the string
std::string ByteString::hex_str() const
{
	std::string rv;
	char hex[3];

	for (size_t i = 0; i < byteString.size(); i++)
	{
		sprintf(hex, "%02X", byteString[i]);

		rv += hex;
	}

	return rv;
}

// Return the long value
unsigned long ByteString::long_val() const
{
	// Convert the first 8 bytes of the string to an unsigned long value
	unsigned long rv = 0;

	for (size_t i = 0; i < std::min(size_t(8), byteString.size()); i++)
	{
		rv <<= 8;
		rv += byteString[i];
	}

	return rv;
}

// Cut of the first part of the string and convert it to a long value
unsigned long ByteString::firstLong()
{
	unsigned long rv = long_val();

	split(8);

	return rv;
}

// Split of the specified part of the string as a separate byte string
ByteString ByteString::split(size_t len)
{
	ByteString rv = substr(0, len);

	size_t newSize = (byteString.size() > len) ? (byteString.size() - len) : 0;

	if (newSize > 0)
	{
		for (size_t i = 0; i < newSize; i++)
		{
			byteString[i] = byteString[i + len];
		}
	}

	byteString.resize(newSize);

	return rv;
}

// The size of the byte string in bits
size_t ByteString::bits() const
{
	size_t bits = byteString.size() * 8;

	if (bits == 0) return 0;

	for (size_t i = 0; i < byteString.size(); i++)
	{
		unsigned char byte = byteString[i];

		for (unsigned char mask = 0x80; mask > 0; mask >>= 1)
		{
			if ((byte & mask) == 0)
			{
				bits--;
			}
			else
			{
				return bits;
			}
		}
	}

	return bits;
}

// The size of the byte string in bytes
size_t ByteString::size() const
{
	return byteString.size();
}

void ByteString::resize(const size_t newSize)
{
	byteString.resize(newSize);
}

void ByteString::wipe(const size_t newSize /* = 0 */)
{
	this->resize(newSize);

	if (!byteString.empty())
		memset(&byteString[0], 0x00, byteString.size());
}

// Comparison
bool ByteString::operator==(const ByteString& compareTo) const
{
	if (compareTo.size() != this->size())
	{
		return false;
	}
	else if (this->size() == 0)
	{
		return true;
	}

	return (memcmp(&byteString[0], &compareTo.byteString[0], this->size()) == 0);
}

bool ByteString::operator!=(const ByteString& compareTo) const
{
	if (compareTo.size() != this->size())
	{
		return true;
	}
	else if (this->size() == 0)
	{
		return false;
	}

	return (memcmp(&byteString[0], &compareTo.byteString[0], this->size()) != 0);
}

// XOR data
ByteString operator^(const ByteString& lhs, const ByteString& rhs)
{
	size_t xorLen = std::min(lhs.size(), rhs.size());
	ByteString rv;

	for (size_t i = 0; i < xorLen; i++)
	{
		rv += lhs.const_byte_str()[i] ^ rhs.const_byte_str()[i];
	}

	return rv;
}

// Serialisation/deserialisation
ByteString ByteString::serialise() const
{
	ByteString len((unsigned long) size());

	return len + *this;
}

/* static */ ByteString ByteString::chainDeserialise(ByteString& serialised)
{
	size_t len = (size_t) serialised.firstLong();

	ByteString rv = serialised.split(len);

	return rv;
}

