/*
	PIP - Platform Independent Primitives
	Digest algorithms
	Ivan Pelipenko peri4ko@yandex.ru

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	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 Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "pidigest.h"

#include "pidigest_blake2_p.h"
#include "pidigest_md2_p.h"
#include "pidigest_md4_p.h"
#include "pidigest_md5_p.h"
#include "pidigest_sha1_p.h"
#include "pidigest_sha2_p.h"
#include "pidigest_siphash_p.h"


int PIDigest::hashLength(Type type) {
	switch (type) {
	case Type::MD2: return 16;
	case Type::MD4: return 16;
	case Type::MD5: return 16;
	case Type::SHA1: return 20;
	case Type::SHA2_224: return 28;
	case Type::SHA2_256: return 32;
	case Type::SHA2_384: return 48;
	case Type::SHA2_512: return 64;
	case Type::SHA2_512_224: return 28;
	case Type::SHA2_512_256: return 32;
	case Type::BLAKE2s_128: return 16;
	case Type::BLAKE2s_160: return 20;
	case Type::BLAKE2s_224: return 28;
	case Type::BLAKE2s_256: return 32;
	case Type::BLAKE2b_128: return 16;
	case Type::BLAKE2b_160: return 20;
	case Type::BLAKE2b_224: return 28;
	case Type::BLAKE2b_256: return 32;
	case Type::BLAKE2b_384: return 48;
	case Type::BLAKE2b_512: return 64;
	case Type::SipHash_2_4_64: return 8;
	case Type::SipHash_2_4_128: return 16;
	case Type::HalfSipHash_2_4_32: return 4;
	case Type::HalfSipHash_2_4_64: return 8;
	default: break;
	}
	return 0;
}


int PIDigest::blockLength(Type type) {
	switch (type) {
	case Type::MD2: return 16;
	case Type::MD4:
	case Type::MD5: return 64;
	case Type::SHA1: return 64;
	case Type::SHA2_224:
	case Type::SHA2_256: return 64;
	case Type::SHA2_384:
	case Type::SHA2_512:
	case Type::SHA2_512_224:
	case Type::SHA2_512_256: return 128;
	case Type::BLAKE2s_128:
	case Type::BLAKE2s_160:
	case Type::BLAKE2s_224:
	case Type::BLAKE2s_256: return 64;
	case Type::BLAKE2b_128:
	case Type::BLAKE2b_160:
	case Type::BLAKE2b_224:
	case Type::BLAKE2b_256:
	case Type::BLAKE2b_384:
	case Type::BLAKE2b_512: return 128;
	case Type::SipHash_2_4_64: return 8;
	case Type::SipHash_2_4_128: return 16;
	case Type::HalfSipHash_2_4_32: return 4;
	case Type::HalfSipHash_2_4_64: return 8;
	default: break;
	}
	return 0;
}


PIConstChars PIDigest::typeName(Type type) {
	switch (type) {
	case Type::MD2: return "MD2";
	case Type::MD4: return "MD4";
	case Type::MD5: return "MD5";
	case Type::SHA1: return "SHA1";
	case Type::SHA2_224: return "SHA2_224";
	case Type::SHA2_256: return "SHA2_256";
	case Type::SHA2_384: return "SHA2_384";
	case Type::SHA2_512: return "SHA2_512";
	case Type::SHA2_512_224: return "SHA2_512_224";
	case Type::SHA2_512_256: return "SHA2_512_256";
	case Type::BLAKE2s_128: return "BLAKE2s_128";
	case Type::BLAKE2s_160: return "BLAKE2s_160";
	case Type::BLAKE2s_224: return "BLAKE2s_224";
	case Type::BLAKE2s_256: return "BLAKE2s_256";
	case Type::BLAKE2b_128: return "BLAKE2b_128";
	case Type::BLAKE2b_160: return "BLAKE2b_160";
	case Type::BLAKE2b_224: return "BLAKE2b_224";
	case Type::BLAKE2b_256: return "BLAKE2b_256";
	case Type::BLAKE2b_384: return "BLAKE2b_384";
	case Type::BLAKE2b_512: return "BLAKE2b_512";
	case Type::SipHash_2_4_64: return "SipHash_2_4_64";
	case Type::SipHash_2_4_128: return "SipHash_2_4_128";
	case Type::HalfSipHash_2_4_32: return "HalfSipHash_2_4_32";
	case Type::HalfSipHash_2_4_64: return "HalfSipHash_2_4_64";
	default: break;
	}
	return "Unknown";
}


PIByteArray PIDigest::calculate(const PIByteArray & msg, Type type) {
	switch (type) {
	case Type::MD2: return MD2::md2(msg);
	case Type::MD4: return MD4::md4(msg);
	case Type::MD5: return MD5::md5(msg);
	case Type::SHA1: return SHA1::sha1(msg);
	case Type::SHA2_224: return SHA2::sha2xx(msg, SHA2::initial_224, 28);
	case Type::SHA2_256: return SHA2::sha2xx(msg, SHA2::initial_256, 32);
	case Type::SHA2_384: return SHA2::sha5xx(msg, SHA2::initial_384, 48);
	case Type::SHA2_512: return SHA2::sha5xx(msg, SHA2::initial_512, 64);
	case Type::SHA2_512_224: return SHA2::sha5xx(msg, SHA2::initial_512_224, 28);
	case Type::SHA2_512_256: return SHA2::sha5xx(msg, SHA2::initial_512_256, 32);
	case Type::BLAKE2s_128: return BLAKE2::blake2s(msg, {}, 16);
	case Type::BLAKE2s_160: return BLAKE2::blake2s(msg, {}, 20);
	case Type::BLAKE2s_224: return BLAKE2::blake2s(msg, {}, 28);
	case Type::BLAKE2s_256: return BLAKE2::blake2s(msg, {}, 32);
	case Type::BLAKE2b_128: return BLAKE2::blake2b(msg, {}, 16);
	case Type::BLAKE2b_160: return BLAKE2::blake2b(msg, {}, 20);
	case Type::BLAKE2b_224: return BLAKE2::blake2b(msg, {}, 28);
	case Type::BLAKE2b_256: return BLAKE2::blake2b(msg, {}, 32);
	case Type::BLAKE2b_384: return BLAKE2::blake2b(msg, {}, 48);
	case Type::BLAKE2b_512: return BLAKE2::blake2b(msg, {}, 64);
	case Type::SipHash_2_4_64: return SipHash::siphash(msg, {}, 8);
	case Type::SipHash_2_4_128: return SipHash::siphash(msg, {}, 16);
	case Type::HalfSipHash_2_4_32: return SipHash::halfsiphash(msg, {}, 4);
	case Type::HalfSipHash_2_4_64: return SipHash::halfsiphash(msg, {}, 8);
	default: break;
	}
	return {};
}


PIByteArray PIDigest::calculateWithKey(const PIByteArray & msg, const PIByteArray & key, Type type) {
	switch (type) {
	case Type::BLAKE2s_128: return BLAKE2::blake2s(msg, key, 16);
	case Type::BLAKE2s_160: return BLAKE2::blake2s(msg, key, 20);
	case Type::BLAKE2s_224: return BLAKE2::blake2s(msg, key, 28);
	case Type::BLAKE2s_256: return BLAKE2::blake2s(msg, key, 32);
	case Type::BLAKE2b_128: return BLAKE2::blake2b(msg, key, 16);
	case Type::BLAKE2b_160: return BLAKE2::blake2b(msg, key, 20);
	case Type::BLAKE2b_224: return BLAKE2::blake2b(msg, key, 28);
	case Type::BLAKE2b_256: return BLAKE2::blake2b(msg, key, 32);
	case Type::BLAKE2b_384: return BLAKE2::blake2b(msg, key, 48);
	case Type::BLAKE2b_512: return BLAKE2::blake2b(msg, key, 64);
	case Type::SipHash_2_4_64: return SipHash::siphash(msg, key, 8);
	case Type::SipHash_2_4_128: return SipHash::siphash(msg, key, 16);
	case Type::HalfSipHash_2_4_32: return SipHash::halfsiphash(msg, key, 4);
	case Type::HalfSipHash_2_4_64: return SipHash::halfsiphash(msg, key, 8);
	default: break;
	}
	return {};
}


PIByteArray PIDigest::HMAC(const PIByteArray & msg, const PIByteArray & key, Type type) {
	int b     = PIDigest::blockLength(type);
	auto ipad = PIByteArray(b, uchar(0x36));
	auto opad = PIByteArray(b, uchar(0x5C));
	PIByteArray k0;
	if (key.size_s() > b)
		k0 = PIDigest::calculate(key, type);
	else
		k0 = key;
	k0.resize(b);
	return PIDigest::calculate((k0 ^ opad).append(PIDigest::calculate((k0 ^ ipad).append(msg), type)), type);
}
