/**
 * \file asn1ec.c
 * \brief Parser of EC key ASN.1 representation.
 * \author Dmytro Podgornyi (d.podgornyi@samsung.com)
 * \version 0.1
 * \date Created May 28, 2013
 * \par In Samsung Ukraine R&D Center (SURC) under a contract between
 * \par LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) and
 * \par "Samsung Elecrtronics Co", Ltd (Seoul, Republic of Korea)
 * \par Copyright: (c) Samsung Electronics Co, Ltd 2012. All rights reserved.
 **/

#include <stdint.h>
#include <string.h>

#include "CommLayerData.h"
#include "log.h"

#include "asn1ec.h"

int32_t ec_public_key_parse(const uint8_t *buf, size_t len, EC_KEY *ec)
{
	int32_t res = PLATFORM_INTERNAL_ERROR;
	BIGNUM X = {0}, Y = {0};

	if (!ec) {
		LOGE("ec_public_key_parse: key is NULL");
		return WRONG_DATA;
	}
	if (len < 1 || buf[0] != POINT_CONVERSION_UNCOMPRESSED) {
		LOGE("ec_public_key_parse: it is not an uncompressed point");
		return WRONG_DATA;
	}

	--len;
	++buf;
	if (len == 0 || len % 2 != 0) {
		LOGE("ec_public_key_parse: bad size of key: %d is not even", len);
		return WRONG_DATA;
	}

	if (!ec->pub_key) {
		if (!ec->group) {
			LOGE("ec_public_key_parse: ec->group not set");
			return PLATFORM_INTERNAL_ERROR;
		}
		ec->pub_key = EC_POINT_new(ec->group);
		if (!ec->pub_key) {
			LOGE("ec_public_key_parse: couldn't allocate ECPoint");
			return PLATFORM_INTERNAL_ERROR;
		}
	}

	BN_init(&X);
	BN_init(&Y);

	len /= 2;
	if (BN_bin2bn(buf, len, &X) == NULL) {
		LOGE("ec_public_key_parse: couldn't read X");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}
	buf += len;
	if (BN_bin2bn(buf, len, &Y) == NULL) {
		LOGE("ec_public_key_parse: couldn't read Y");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	if (!EC_POINT_set_affine_coordinates_GFp(ec->group, ec->pub_key, &X, &Y, NULL)) {
		LOGE("ec_public_key_parse: EC_POINT_set_affine_coordinates_GFp failed");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	res = NO_ERROR;

cleanup:
	BN_free(&X);
	BN_free(&Y);

	return res;
}

int32_t ec_private_key_parse(const uint8_t *buf, size_t len, EC_KEY *ec)
{
	struct asn1_hdr hdr = {0};
	const uint8_t *buf_end = NULL;

	if (!ec) {
		return WRONG_DATA;
	}

	buf_end = buf + len;

	if (!ec->priv_key) {
		ec->priv_key = BN_new();
		if (!ec->priv_key) {
			return PLATFORM_INTERNAL_ERROR;
		}
	}

	if (asn1_get_next(buf, buf_end - buf, &hdr) ||
		hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) {
		return WRONG_DATA;
	}

	buf = hdr.payload;
	if (asn1_get_next(buf, buf_end - buf, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
		return WRONG_DATA;
	}

	buf = hdr.payload + hdr.length;
	if (asn1_get_next(buf, buf_end - buf, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OCTETSTRING) {
		return WRONG_DATA;
	}

	if (BN_bin2bn(hdr.payload, hdr.length, ec->priv_key) == NULL) {
		return PLATFORM_INTERNAL_ERROR;
	}

	return NO_ERROR;
}