/*
 * asn1rsa.c
 *
 *  Created on: May 16, 2013
 *      Author: ignat
 */

#include <stdint.h>
#include "asn1rsa.h"

rsa_parse_error_t rsa_private_key_parse(const uint8_t *buf, size_t len, RSA *ctx)
{
	struct asn1_hdr hdr = {0};
	const uint8_t *key_end = NULL;
	const uint8_t *pos = NULL;

	if (!buf || !len || !ctx) {
		return RSA_PARSE_ERROR;
	}

	if (!ctx->n)
		ctx->n = BN_new();
	if (!ctx->e)
		ctx->e = BN_new();
	if (!ctx->d)
		ctx->d = BN_new();
	if (!ctx->p)
		ctx->p = BN_new();
	if (!ctx->q)
		ctx->q = BN_new();
	if (!ctx->dmp1)
		ctx->dmp1 = BN_new();
	if (!ctx->dmq1)
		ctx->dmq1 = BN_new();
	if (!ctx->iqmp)
		ctx->iqmp = BN_new();
	if (!ctx->n || !ctx->e || !ctx->d || !ctx->p || !ctx->q || !ctx->dmp1 || !ctx->dmq1 || !ctx->iqmp) {
		return RSA_PARSE_ERROR;
	}

	if (asn1_get_next(buf, len, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) {
		return RSA_PARSE_ERROR;
	}

	key_end = hdr.payload + hdr.length;

	/* version */

	if (asn1_get_next(hdr.payload, hdr.length, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
		return RSA_PARSE_ERROR;
	}

	pos = hdr.payload + hdr.length;

	/* N */

	if (asn1_get_next(pos, key_end - pos, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
		return RSA_PARSE_ERROR;
	}

	if (0x00 == hdr.payload[0]) {
		if (BN_bin2bn(hdr.payload + 1, hdr.length - 1, ctx->n) == NULL) {
			goto out_parse_err;
		}
	} else {
		if (BN_bin2bn(hdr.payload, hdr.length, ctx->n) == NULL) {
			goto out_parse_err;
		}
	}

	pos = hdr.payload + hdr.length;

	/* E */

	if (asn1_get_next(pos, key_end - pos, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
		return RSA_PARSE_ERROR;
	}

	if (0x00 == hdr.payload[0]) {
		if (BN_bin2bn(hdr.payload + 1, hdr.length - 1, ctx->e) == NULL) {
			goto out_parse_err;
		}
	} else {
		if (BN_bin2bn(hdr.payload, hdr.length, ctx->e) == NULL) {
			goto out_parse_err;
		}
	}

	pos = hdr.payload + hdr.length;

	/* D */

	if (asn1_get_next(pos, key_end - pos, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
		return RSA_PARSE_ERROR;
	}

	if (0x00 == hdr.payload[0]) {
		if (BN_bin2bn(hdr.payload + 1, hdr.length - 1, ctx->d) == NULL) {
			goto out_parse_err;
		}
	} else {
		if (BN_bin2bn(hdr.payload, hdr.length, ctx->d) == NULL) {
			goto out_parse_err;
		}
	}

	pos = hdr.payload + hdr.length;

	/* P */

	if (asn1_get_next(pos, key_end - pos, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
		goto out_parse_err;
	}

	if (0x00 == hdr.payload[0]) {
		if (BN_bin2bn(hdr.payload + 1, hdr.length - 1, ctx->p) == NULL) {
			goto out_parse_err;
		}
	} else {
		if (BN_bin2bn(hdr.payload, hdr.length, ctx->p) == NULL) {
			goto out_parse_err;
		}
	}
	pos = hdr.payload + hdr.length;

	/* Q */

	if (asn1_get_next(pos, key_end - pos, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
		goto out_parse_err;
	}

	if (0x00 == hdr.payload[0]) {
		if (BN_bin2bn(hdr.payload + 1, hdr.length - 1, ctx->q) == NULL) {
			goto out_parse_err;
		}
	} else {
		if (BN_bin2bn(hdr.payload, hdr.length, ctx->q) == NULL) {
			goto out_parse_err;
		}
	}
	pos = hdr.payload + hdr.length;

	/* dP */

	if (asn1_get_next(pos, key_end - pos, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
		goto out_parse_err;
	}

	if (0x00 == hdr.payload[0]) {
		if (BN_bin2bn(hdr.payload + 1, hdr.length - 1, ctx->dmp1) == NULL) {
			goto out_parse_err;
		}
	} else {
		if (BN_bin2bn(hdr.payload, hdr.length, ctx->dmp1) == NULL) {
			goto out_parse_err;
		}
	}
	pos = hdr.payload + hdr.length;

	/* dQ */

	if (asn1_get_next(pos, key_end - pos, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
		goto out_parse_err;
	}

	if (0x00 == hdr.payload[0]) {
		if (BN_bin2bn(hdr.payload + 1, hdr.length - 1, ctx->dmq1) == NULL) {
			goto out_parse_err;
		}
	} else {
		if (BN_bin2bn(hdr.payload, hdr.length, ctx->dmq1) == NULL) {
			goto out_parse_err;
		}
	}
	pos = hdr.payload + hdr.length;

	/* qP */

	if (asn1_get_next(pos, key_end - pos, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
		goto out_parse_err;
	}

	if (0x00 == hdr.payload[0]) {
		if (BN_bin2bn(hdr.payload + 1, hdr.length - 1, ctx->iqmp) == NULL) {
			goto out_parse_err;
		}
	} else {
		if (BN_bin2bn(hdr.payload, hdr.length, ctx->iqmp) == NULL) {
			goto out_parse_err;
		}
	}

	return RSA_PARSE_OK;

out_parse_err:
	return RSA_PARSE_ERROR;
}

rsa_parse_error_t rsa_public_key_parse(const uint8_t *buf, size_t len, RSA *ctx)
{
	struct asn1_hdr hdr = {0};
	const uint8_t *key_end = NULL;
	const uint8_t *pos = NULL;

	if (!buf || !len || !ctx) {
		return RSA_PARSE_ERROR;
	}

	if (!ctx->n)
		ctx->n = BN_new();
	if (!ctx->e)
		ctx->e = BN_new();
	if (!ctx->n || !ctx->e) {
		return RSA_PARSE_ERROR;
	}

	if (asn1_get_next(buf, len, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) {
		return RSA_PARSE_ERROR;
	}

	key_end = hdr.payload + hdr.length;

	if (asn1_get_next(hdr.payload, hdr.length, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
		return RSA_PARSE_ERROR;
	}

	if (0x00 == hdr.payload[0]) {
		if (BN_bin2bn(hdr.payload + 1, hdr.length - 1, ctx->n) == NULL) {
			goto out_parse_err;
		}
	} else {
		if (BN_bin2bn(hdr.payload, hdr.length, ctx->n) == NULL) {
			goto out_parse_err;
		}
	}

	pos = hdr.payload + hdr.length;
	if (asn1_get_next(pos, key_end - pos, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
		goto out_parse_err;
	}

	if (BN_bin2bn(hdr.payload, hdr.length, ctx->e) == NULL) {
		goto out_parse_err;
	}

	return RSA_PARSE_OK;

out_parse_err:
	return RSA_PARSE_ERROR;
}