/**
 * Copyright 2020 German Tischler-Höhle
 *
 * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 COPYRIGHT
 * HOLDER OR CONTRIBUTORS 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.
 **/
#if ! defined(LIBSECRECY_GCMIV_HPP)
#define LIBSECRECY_GCMIV_HPP

#include <libsecrecy/Yarrow.hpp>
#include <nettle/gcm.h>

namespace libsecrecy
{
	/**
	 * initialization vector for GCM
	 **/
	struct GCMIV : public NumToHex
	{
		private:
		LockedMemory<uint8_t> L;

		public:
		/**
		 * initialize with zeroes only
		 **/
		GCMIV()
		: L(GCM_IV_SIZE)
		{
			std::fill(L.begin(),L.end(),0);
		}
		/**
		 * initialize from random sequence
		 **/
		GCMIV(Yarrow & Y)
		: L(GCM_IV_SIZE)
		{
			Y(L.get(),GCM_IV_SIZE);
		}
		/**
		 * initialize from other IV
		 **/
		GCMIV(GCMIV const & K)
		: L(GCM_IV_SIZE)
		{
			std::copy(K.L.begin(),K.L.end(),L.begin());
		}
		/**
		 * copy from other IV
		 **/
		GCMIV & operator=(GCMIV const & K)
		{
			if ( this != &K )
				std::copy(K.L.begin(),K.L.end(),L.begin());
			return *this;
		}

		/**
		 * convert to hex char sequence
		 **/
		std::shared_ptr< LockedMemory<char> > toHex() const
		{
			return L.toHex();
		}

		std::string toHexString() const
		{
			std::shared_ptr< LockedMemory<char> > H(toHex());
			return std::string(H->begin(),H->end());
		}

		uint8_t const * get() const
		{
			return L.get();
		}

		uint8_t const * begin() const
		{
			return get();
		}

		template<typename iterator>
		void set(iterator const a, iterator const e)
		{
			if ( (e - a) != static_cast<std::ptrdiff_t>(size()) )
				throw std::runtime_error("libsecrecy::GCMIV::set: input region has invalid size");

			std::copy(a,e,L.get());
		}

		std::size_t size() const
		{
			return L.size();
		}

		/**
		 * write to stream
		 **/
		std::size_t write(std::ostream & out)
		{
			static_assert(sizeof(uint8_t) == sizeof(char));
			out.write(reinterpret_cast<char const *>(begin()),size());

			if ( ! out )
			{
				throw std::runtime_error("libsecrecy::GCMIV::write: failed to write to stream");
			}

			return size();
		}
	};
};
#endif
