/*
 * Copyright (c)2013-2020 ZeroTier, Inc.
 *
 * Use of this software is governed by the Business Source License included
 * in the LICENSE.TXT file in the project's root directory.
 *
 * Change Date: 2024-01-01
 *
 * On the date above, in accordance with the Business Source License, use
 * of this software will be governed by version 2.0 of the Apache License.
 */
/****/

#include "Dictionary.hpp"
#include "SHA512.hpp"

namespace ZeroTier {

Dictionary::Dictionary()
{
}

Vector<uint8_t> &Dictionary::operator[](const char *k)
{
	return m_entries[s_key(k)];
}

const Vector<uint8_t> &Dictionary::operator[](const char *k) const
{
	static const Vector<uint8_t> s_emptyEntry;
	SortedMap< String, Vector<uint8_t> >::const_iterator e(m_entries.find(s_key(k)));
	return (e == m_entries.end()) ? s_emptyEntry : e->second;
}

void Dictionary::add(const char *k, bool v)
{
	Vector<uint8_t> &e = (*this)[k];
	e.resize(2);
	e[0] = (uint8_t) (v ? '1' : '0');
	e[1] = 0;
}

void Dictionary::add(const char *k, const Address &v)
{
	Vector<uint8_t> &e = (*this)[k];
	e.resize(ZT_ADDRESS_STRING_SIZE_MAX);
	v.toString((char *) e.data());
}

void Dictionary::add(const char *k, const char *v)
{
	if ((v) && (*v)) {
		Vector<uint8_t> &e = (*this)[k];
		e.clear();
		while (*v)
			e.push_back((uint8_t) *(v++));
	}
}

void Dictionary::add(const char *k, const void *data, unsigned int len)
{
	Vector<uint8_t> &e = (*this)[k];
	if (len != 0) {
		e.assign((const uint8_t *) data, (const uint8_t *) data + len);
	} else {
		e.clear();
	}
}

bool Dictionary::getB(const char *k, bool dfl) const
{
	const Vector<uint8_t> &e = (*this)[k];
	if (!e.empty()) {
		switch ((char) e[0]) {
			case '1':
			case 't':
			case 'T':
			case 'y':
			case 'Y':
				return true;
			default:
				return false;
		}
	}
	return dfl;
}

uint64_t Dictionary::getUI(const char *k, uint64_t dfl) const
{
	uint8_t tmp[18];
	uint64_t v = dfl;
	const Vector<uint8_t> &e = (*this)[k];
	if (!e.empty()) {
		if (e.back() != 0) {
			const unsigned long sl = e.size();
			Utils::copy(tmp, e.data(), (sl > 17) ? 17 : sl);
			tmp[17] = 0;
			return Utils::unhex((const char *) tmp);
		}
		return Utils::unhex((const char *) e.data());
	}
	return v;
}

char *Dictionary::getS(const char *k, char *v, const unsigned int cap) const
{
	if (cap == 0) // sanity check
		return v;
	const Vector<uint8_t> &e = (*this)[k];
	unsigned int i = 0;
	const unsigned int last = cap - 1;
	for (;;) {
		if ((i == last) || (i >= (unsigned int)e.size()))
			break;
		v[i] = (char) e[i];
		++i;
	}
	v[i] = 0;
	return v;
}

void Dictionary::clear()
{
	m_entries.clear();
}

void Dictionary::encode(Vector<uint8_t> &out, const bool omitSignatureFields) const
{
	out.clear();
	for (SortedMap< String, Vector<uint8_t> >::const_iterator ti(m_entries.begin());ti != m_entries.end();++ti) {
		if ((!omitSignatureFields) || ((ti->first != ZT_DICTIONARY_SIGNATURE_KEY))) {
			s_appendKey(out, ti->first.data());
			for (Vector<uint8_t>::const_iterator i(ti->second.begin());i != ti->second.end();++i)
				s_appendValueByte(out, *i);
			out.push_back((uint8_t) '\n');
		}
	}
	out.push_back(0);
}

bool Dictionary::decode(const void *data, unsigned int len)
{
	clear();
	String k;
	Vector<uint8_t> *v = nullptr;
	bool escape = false;
	for (unsigned int di = 0;di < len;++di) {
		uint8_t c = reinterpret_cast<const uint8_t *>(data)[di];
		if (!c) break;
		if (v) {
			if (escape) {
				escape = false;
				switch (c) {
					case 48:
						v->push_back(0);
						break;
					case 101:
						v->push_back(61);
						break;
					case 110:
						v->push_back(10);
						break;
					case 114:
						v->push_back(13);
						break;
					default:
						v->push_back(c);
						break;
				}
			} else {
				if (c == (uint8_t) '\n') {
					k.clear();
					v = nullptr;
				} else if (c == 92) { // backslash
					escape = true;
				} else {
					v->push_back(c);
				}
			}
		} else {
			if ((c < 33) || (c > 126) || (c == 92)) {
				return false;
			} else if (c == (uint8_t) '=') {
				k.push_back(0);
				v = &m_entries[k];
			} else if (k.size() < 7) {
				k.push_back(c);
			} else {
				return false;
			}
		}
	}
	return true;
}

void Dictionary::sign(
	const uint8_t c25519PrivateKey[ZT_C25519_COMBINED_PRIVATE_KEY_SIZE],
	const uint8_t c25519PublicKey[ZT_C25519_COMBINED_PUBLIC_KEY_SIZE],
	const uint8_t p384PrivateKey[ZT_ECC384_PRIVATE_KEY_SIZE],
	const uint8_t p384PublicKey[ZT_ECC384_PUBLIC_KEY_SIZE])
{
	Vector<uint8_t> buf;
	encode(buf, true);

	uint8_t c25519Signature[ZT_C25519_SIGNATURE_LEN];
	C25519::sign(c25519PrivateKey, c25519PublicKey, buf.data(), (unsigned int)buf.size(), c25519Signature);

	uint8_t hbuf[ZT_ECC384_SIGNATURE_HASH_SIZE];
	static_assert(ZT_ECC384_SIGNATURE_HASH_SIZE == ZT_SHA384_DIGEST_SIZE,"size mismatch");
	SHA384(hbuf, buf.data(), (unsigned int)buf.size());
	uint8_t p384Signature[ZT_ECC384_SIGNATURE_SIZE];
	ECC384ECDSASign(p384PrivateKey, hbuf, p384Signature);

	SHA384(hbuf, c25519PublicKey, ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, p384PublicKey, ZT_ECC384_PUBLIC_KEY_SIZE);

	Dictionary signature;
	signature["kh"].assign(hbuf, hbuf + ZT_SHA384_DIGEST_SIZE);
	signature["ed25519"].assign(c25519Signature, c25519Signature + ZT_C25519_SIGNATURE_LEN);
	signature["p384"].assign(p384Signature, p384Signature + ZT_ECC384_SIGNATURE_SIZE);
	signature.encode((*this)[ZT_DICTIONARY_SIGNATURE_KEY], true);
}

bool Dictionary::verify(
	const uint8_t c25519PublicKey[ZT_C25519_COMBINED_PUBLIC_KEY_SIZE],
	const uint8_t p384PublicKey[ZT_ECC384_PUBLIC_KEY_SIZE]) const
{
	try {
		const Vector< uint8_t > &data = (*this)[ZT_DICTIONARY_SIGNATURE_KEY];
		if (data.empty())
			return false;
		Dictionary signature;
		if (!signature.decode(data.data(), (unsigned int)data.size()))
			return false;
		const Vector< uint8_t > &p384Signature = signature["p384"];
		const Vector< uint8_t > &c25519Signature = signature["ed25519"];
		if ((p384Signature.size() != ZT_ECC384_SIGNATURE_SIZE) || (c25519Signature.size() != ZT_C25519_SIGNATURE_LEN))
			return false;

		Vector< uint8_t > buf;
		encode(buf, true);

		if (C25519::verify(c25519PublicKey, buf.data(), (unsigned int)buf.size(), c25519Signature.data(), (unsigned int)c25519Signature.size())) {
			uint8_t hbuf[ZT_ECC384_SIGNATURE_HASH_SIZE];
			SHA384(hbuf, buf.data(), (unsigned int)buf.size());
			return ECC384ECDSAVerify(p384PublicKey, hbuf, p384Signature.data());
		}
	} catch ( ... ) {}
	return false;
}

} // namespace ZeroTier